On the left side we have a normal JSON formatted object, on the right side we have a normal AL implementation of JSON generation
Item JSON Object
{
itemNo: "123",
price: 10.25,
description: "Insane Item",
...
...
...
active: true,
variants: [
{
description: "Total insane variant"
},
{
description: "not so insane variant"
}
]
}
Normal AL Example
procedure GenerateJSON(item: Record Item)
var
object: JsonObject;
arrElement: JsonObject;
arr: JsonArray;
variants: Record "Item Variant";
begin
object.Add('itemNo', item."No.");
object.Add('price', item."Unit Price");
object.Add('description', item.Description);
//...
//...
//...
object.Add('active', not item.Blocked);
variants.SetFilter("Item No.", item."No.");
if variants.Find('-') then
repeat
Clear(arrElement);
arrElement.Add('description', variants.Description);
arr.Add(arrElement);
until (variants.Next() = 0);
if (arr.Count > 0) then
object.Add('variants', arr);
end;
Looks nice and easy to me, but what if your JSON gets more complicated and you need to reuse JSON blocks more often? Then you have to think about structure. I was looking for a flexible code structure which is easy adobtable.
The second point a was thinking about was, wouldn’t it be nice to just pass an object to a JSON Codeunit and the magic is happening behind?
Could we reduce the above code to the following?
item.ToJsonObject();
We are developers, of course we can.
The Components of the pattern
Assuming we are in a modern development every object (codeunit) knows how to convert to JSON. For that reason the first component is an interface “IJsonObjectConverter” which is implemented by every “object” codeunit.
interface IJsonObjectConverter
{
procedure ToJsonObject(): JsonObject;
}
Since records can not implement interfaces we need a codeunit for the object. But to get the connection from the record to the codeunit I created a second interface
interface "IRecordToObjectConverter"
{
procedure ConvertFromRecord(var recRef: RecordRef);
}
Now I can create the codeunit “ItemObject”
codeunit 50000 "ItemObject" implements IJsonObjectConverter, IRecordToObjectConverter
{
...Getter and setter...
...Some other stuff for another part of this blog
...
procedure ToJsonObject() object: JsonObject;
begin
if GetItemNo() <> '' then
object.Add('itemNo', itemNo);
if GetPrice() <> 0.0 then
object.Add('price', price);
if GetDescription() <> '' then
object.Add('description', description);
if GetActive() <> true then
object.Add('active', active);
end;
procedure ConvertFromRecord(var recRef: RecordRef);
var
Item: Record Item;
begin
if recRef.Number <> Database::Item then begin
Error(errArgumentException);
end;
recRef.SetTable(Item);
SetItemNo(Item."No.");
SetDescription(Item.Description);
SetPrice(Item."Unit Price");
SetActive(not Item.Blocked);
end;
var
itemNo: Code[20];
price: Decimal;
description: Text;
active: Boolean;
}
Now we can export an item to json, but we want also add some variants. For this case we of course also need a codeunit for Item Variants
codeunit 50005 ItemVariantObject implements IJsonObjectConverter, IRecordToObjectConverter
{
...Getter and setter...
...Some other stuff for another part of this blog
...
procedure ToJsonObject() object: JsonObject;
begin
if GetDescription() <> '' then
object.Add('description', description);
end;
procedure ConvertFromRecord(var recRef: RecordRef);
var
ItemVariant: Record "Item Variant";
begin
if recRef.Number <> Database::"Item Variant" then begin
Error(errArgumentException);
end;
recRef.SetTable(ItemVariant);
SetDescription(ItemVariant.Description);
end;
var
description: Text;
active: Boolean;
}
I’m now also able to export Variants, but I need the link to the items to work. For this reason I add a new procedure “GetVariants” to the ItemObject. This procedure returns an JsonObjectList, I will descripe it in part 2 of this blogpost.
And of course adapt the procedure “ToJsonObject” from “ItemObject” to also export Variants
procedure GetVariants() list: Codeunit JsonObjectlist;
var
ItemVariant: Record "Item Variant";
ItemVariantObject: Codeunit ItemVariantObject;
recRef: RecordRef;
begin
ItemVariant.SetRange("Item No.", GetItemNo());
ItemVariant.SetLoadFields(Description);
if ItemVariant.Find('-') then
repeat
recRef.GetTable(ItemVariant);
ItemVariantObject := ItemVariantObject.Create();
ItemVariantObject.ConvertFromRecord(recRef);
list.AddNode(ItemVariantObject, ItemVariantObject);
until ItemVariant.Next() = 0;
end;
procedure ToJsonObject() object: JsonObject;
begin
if GetItemNo() <> '' then
object.Add('itemNo', itemNo);
if GetPrice() <> 0.0 then
object.Add('price', price);
if GetDescription() <> '' then
object.Add('description', description);
if GetActive() <> true then
object.Add('active', active);
object.Add('variants', GetVariants().ToJsonArray());
end;
Now the procedure from the first example looks like this.
procedure GenerateJSON(item: Record Item)
var
itemObject: Codeunit ItemObject;
recRef: RecordRef;
begin
recRef.GetTable(Item);
itemObject.ConvertFromRecord(recRef);
itemObject.ToJsonObject();
end;
Conclusion
Everything mentionend above looks complicated at the first step. but I will descripe in follow ups why it is better approach.
You can see the whole example from here at GitHub. It also includes some implementations for the next blogpost
https://github.com/PatrickSchiefer/AL-JSON-Management-Demo