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 .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." } } Implementation Details I have the following setup:
Table (sch_schedulerconfiguration) The fields sch_bookingform, sch_hoverdetails, and sch_popupdetails are Blob type with Json subtype. Each Blob field has an OnValidate trigger checking if a value exists. Codeunit (SchedulerConfigurationHandler) WriteJSONToBlob clears the Blob field, commits the change, and then writes the JSON data into an OutStream. ReadJSONFromBlob reads the JSON data from an InStream. 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!
I tried almost all blogs for the Blob datatype and I have tried first add sample static data and all possible way form my side .