I have created a table in Business Central that includes fields with the Blob
data type. When I try to send JSON data to these fields using Postman, I encounter the following error:
Postman API Call:
POST https://api.businesscentral.dynamics.com/v2.0/{TenantID}/Sandbox/api/CJAPI/CJG/v1.0/companies({CompanyID})/sch_schedulerconfiguration
Request Body:
{
"sch_bookingform": "{\"default\": \"booking test 13\"}",
"sch_configurationname": "Default test 15",
"sch_hoverdetails": "{\"tooltip\": \"Appointment Details\"}",
"sch_popupdetails": "{\"title\": \"Booking Info\", \"content\": \"Details about the booking\"}"
}
Error Response:
{
"error": {
"code": "BadRequest",
"message": "Read called with an open stream or text reader. Please close any open streams or text readers before calling Read. CorrelationId: dded16d1-6f2f-4415-8ea3-16601153746c."
}
}
"error": {
"code": "BadRequest",
"message": "Read called with an open stream or text reader. Please close any open streams or text readers before calling Read. CorrelationId: dded16d1-6f2f-4415-8ea3-16601153746c."
}
}
Implementation Details
I have the following setup:
Table (sch_schedulerconfiguration)
- The fields
sch_bookingform
,sch_hoverdetails
, andsch_popupdetails
areBlob
type withJson
subtype. - Each
Blob
field has anOnValidate
trigger checking if a value exists.
Codeunit (SchedulerConfigurationHandler)
WriteJSONToBlob
clears theBlob
field, commits the change, and then writes the JSON data into anOutStream
.ReadJSONFromBlob
reads the JSON data from anInStream
.
API Page (schedulerconfiguration)
- Exposes
sch_schedulerconfiguration
as an API endpoint.
Issue & Assistance Required
- Why am I receiving the error
"Read called with an open stream or text reader"
when posting JSON data? - Is there a proper way to handle JSON in
Blob
fields via API requests? - Any suggestions to modify my implementation so that the API correctly stores and retrieves JSON data in
Blob
fields?
table 80100 sch_schedulerconfiguration
{
DataClassification = ToBeClassified;
Caption = 'Scheduler Configuration';
fields
{
field(1; sch_configurationname; Text[50])
{
DataClassification = ToBeClassified;
Caption = 'Configuarion Unique Name';
}
field(13; sch_bookingform; Blob)
{
DataClassification = ToBeClassified;
Caption = 'Booking Form JSON';
Subtype = Json;
trigger OnValidate()
var
// JSONHandler: Codeunit JSONHandler;
BookingJSON: Text;
begin
// BookingJSON := JSONHandler.GetBookingForm(Rec);
if sch_bookingform.HasValue = true then
Error('Booking Form JSON is required.');
end;
}
field(14; sch_hoverdetails; Blob)
{
DataClassification = ToBeClassified;
Caption = 'Hover Details JSON';
Subtype = Json;
trigger OnValidate()
begin
if sch_hoverdetails.HasValue = true then
Error('Hover Details JSON is required.');
end;
}
field(15; sch_popupdetails; Blob)
{
DataClassification = ToBeClassified;
Caption = 'Popup Details JSON';
Subtype = Json;
trigger OnValidate()
begin
if sch_popupdetails.HasValue = true then
Error('Popup Details JSON is required.');
end;
}
}
keys
{
key(Key1; sch_configurationname)
{
Clustered = true;
}
}
procedure GetBookingFormJSON(): Text
var
ConfigHandler: Codeunit SchedulerConfigurationHandler;
begin
exit(ConfigHandler.ReadJSONFromBlob(Rec, 'sch_bookingform'));
end;
procedure GetHoverDetailsJSON(): Text
var
ConfigHandler: Codeunit SchedulerConfigurationHandler;
begin
exit(ConfigHandler.ReadJSONFromBlob(Rec, 'sch_hoverdetails'));
end;
procedure GetPopupDetailsJSON(): Text
var
ConfigHandler: Codeunit SchedulerConfigurationHandler;
begin
exit(ConfigHandler.ReadJSONFromBlob(Rec, 'sch_popupdetails'));
end;
}
codeunit 80103 SchedulerConfigurationHandler
{
SingleInstance = false;
procedure WriteJSONToBlob(var Rec: Record sch_schedulerconfiguration; JSONData: Text; FieldName: Text)
var
OutStream: OutStream;
begin
// ----- Step 1: Clear the Blob field and commit the change -----
case FieldName of
'sch_bookingform':
Clear(Rec.sch_bookingform);
'sch_hoverdetails':
Clear(Rec.sch_hoverdetails);
'sch_popupdetails':
Clear(Rec.sch_popupdetails);
else
Error('Invalid field name');
end;
// First modify to persist the cleared Blob field.
Rec.Modify(true);
// Optionally, commit the transaction here to force closure of any open streams.
COMMIT;
// ----- Step 2: Open a new OutStream and write the JSON data -----
case FieldName of
'sch_bookingform':
Rec.sch_bookingform.CreateOutStream(OutStream);
'sch_hoverdetails':
Rec.sch_hoverdetails.CreateOutStream(OutStream);
'sch_popupdetails':
Rec.sch_popupdetails.CreateOutStream(OutStream);
else
Error('Invalid field name');
end;
OutStream.WriteText(JSONData);
// OutStream goes out of scope here, which should close it.
// Save the updated Blob data.
Rec.Modify(true);
end;
procedure ReadJSONFromBlob(var Rec: Record sch_schedulerconfiguration; FieldName: Text): Text
var
InStream: InStream;
JSONData: Text;
begin
JSONData := ''; // Initialize to avoid null errors.
// Ensure the correct field's stream is created
case FieldName of
'sch_bookingform':
if Rec.sch_bookingform.HasValue then
Rec.sch_bookingform.CreateInStream(InStream);
'sch_hoverdetails':
if Rec.sch_hoverdetails.HasValue then
Rec.sch_hoverdetails.CreateInStream(InStream);
'sch_popupdetails':
if Rec.sch_popupdetails.HasValue then
Rec.sch_popupdetails.CreateInStream(InStream);
else
Error('Invalid field name');
end;
// Ensure InStream is not null before reading
if not Rec.sch_bookingform.HasValue then
exit(''); // If there's no value in the field, return an empty string
InStream.ReadText(JSONData);
exit(JSONData);
end;
}
page 80100 schedulerconfiguration
{
APIGroup = 'CJG';
APIPublisher = 'CJAPI';
APIVersion = 'v1.0';
ApplicationArea = All;
Caption = 'schedulerconfiguration';
DelayedInsert = true;
EntityName = 'sch_schedulerconfiguration';
EntitySetName = 'sch_schedulerconfiguration';
PageType = API;
SourceTable = sch_schedulerconfiguration;
layout
{
area(Content)
{
repeater(General)
{
field(sch_configurationname; Rec.sch_configurationname)
{
Caption = 'Configuarion Unique Name';
}
field(sch_bookingform; Rec.sch_bookingform)
{
Caption = 'Booking Form JSON';
}
field(sch_hoverdetails; Rec.sch_hoverdetails)
{
Caption = 'Hover Details JSON';
}
field(sch_popupdetails; Rec.sch_popupdetails)
{
Caption = 'Popup Details JSON';
}
}
}
}
actions
{
area(Processing)
{
action(Save)
{
ApplicationArea = All;
Caption = 'Save';
Image = Save;
Promoted = true;
PromotedCategory = Process;
PromotedIsBig = true;
ToolTip = 'Save the JSON data to the configuration.';
trigger OnAction()
var
ConfigHandler: Codeunit SchedulerConfigurationHandler;
begin
// Save each JSON field to the corresponding Blob
ConfigHandler.WriteJSONToBlob(Rec, BookingFormJSONVar, 'sch_bookingform');
ConfigHandler.WriteJSONToBlob(Rec, HoverDetailsJSONVar, 'sch_hoverdetails');
ConfigHandler.WriteJSONToBlob(Rec, PopupDetailsJSONVar, 'sch_popupdetails');
Message('Configuration saved successfully.');
end;
}
}
}
var
BookingFormJSONVar: Text;
HoverDetailsJSONVar: Text;
PopupDetailsJSONVar: Text;
trigger OnAfterGetRecord()
begin
// Load current Blob data into variables when the record is loaded
BookingFormJSONVar := Rec.GetBookingFormJSON();
HoverDetailsJSONVar := Rec.GetHoverDetailsJSON();
PopupDetailsJSONVar := Rec.GetPopupDetailsJSON();
end;
}
Any guidance would be greatly appreciated!
Categories: