Site icon Patrick Schiefer

The object oriented way of JSON handling in AL – Part 1

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

Exit mobile version