Codeunit 5461 Json Impl.

App
System Application
Namespace
System.Text.Json
Versions
17-19, 24-28
App by version
Base Application from 17, System Application from 24
Name by version
Graph Int. - Contact from 17, Json Impl. from 24

Procedures, 31Events, 1

Versions171819202122232425262728

Source242526272829

Source in 29

src/System Application/App/Json/src/JsonImpl.Codeunit.al443 lines, Copyright (c) Microsoft Corporation. MIT

// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------

namespace System.Text.Json;

using System;
using System.Text;
using System.Utilities;

codeunit 5461 "Json Impl."
{
    Access = Internal;
    InherentEntitlements = X;
    InherentPermissions = X;

    var
        SourceWarningLength: Integer;
        JsonArrayDotNet: DotNet JArray;
        JsonObjectDotNet: DotNet JObject;
        LogLimitWarningTxt: Label 'The JSON input length (%1) exceeds the maximum suggested length (%2) for JSON processing.', Locked = true;

    internal procedure EmitLengthWarning(SourceLength: Integer; tag: Text; FormatString: Text)
    begin
        if SourceWarningLength <= 0 then
            SourceWarningLength := 10485760; // 10 * 1024 * 1024 = 10 MB

        if SourceLength > SourceWarningLength then
            Session.LogMessage(tag, StrSubstNo(FormatString, SourceLength, SourceWarningLength), Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::All, 'resources', 'memory');
    end;

    procedure InitializeCollectionFromString(JSONString: Text)
    begin
        Clear(JsonArrayDotNet);
        if JSONString <> '' then begin
            EmitLengthWarning(StrLen(JSONString), '0000QNC', LogLimitWarningTxt);
            JsonArrayDotNet := JsonArrayDotNet.Parse(JSONString)
        end else
            InitializeEmptyCollection();
    end;

    procedure InitializeObjectFromString(JSONString: Text)
    begin
        Clear(JsonObjectDotNet);
        if JSONString <> '' then begin
            EmitLengthWarning(StrLen(JSONString), '0000QND', LogLimitWarningTxt);
            JsonObjectDotNet := JsonObjectDotNet.Parse(JSONString)
        end else
            InitializeEmptyObject();
    end;

    procedure GetCollectionCount(): Integer
    begin
        exit(JsonArrayDotNet.Count);
    end;

    procedure GetCollectionAsText() Value: Text
    begin
        GetCollection().WriteTo(Value);
    end;

    procedure GetCollectionAsText(Indentation: Boolean) Value: Text
    var
        JsonConvert: DotNet JsonConvert;
        Formatting: DotNet Formatting;
    begin
        Value := GetCollectionAsText();

        if Indentation then begin
            JsonArrayDotNet := JsonArrayDotNet.Parse(Value);
            Value := JsonConvert.SerializeObject(JsonArrayDotNet, Formatting.Indented)
        end;
    end;

    procedure GetCollection() JArray: JsonArray
    begin
        JArray.ReadFrom(JsonArrayDotNet.ToString());
    end;

    procedure GetObjectAsText() Value: Text
    begin
        GetObject().WriteTo(Value);
    end;

    procedure GetObject() JObject: JsonObject
    begin
        JObject.ReadFrom(JsonObjectDotNet.ToString());
    end;

    procedure GetObjectFromCollectionByIndex(Index: Integer; var JsonObjectTxt: Text): Boolean
    begin
        if not GetJObjectFromCollectionByIndex(Index) then
            exit(false);

        JsonObjectTxt := JsonObjectDotNet.ToString();
        exit(true);
    end;

    procedure GetValueAndSetToRecFieldNo(RecordRef: RecordRef; PropertyPath: Text; FieldNo: Integer): Boolean
    var
        FieldRef: FieldRef;
    begin
        if IsNull(JsonObjectDotNet) then
            exit(false);

        FieldRef := RecordRef.Field(FieldNo);
        exit(GetPropertyValueFromJObjectByPathSetToFieldRef(PropertyPath, FieldRef));
    end;

    procedure GetPropertyValueFromJObjectByName(propertyName: Text; var value: Variant): Boolean
    var
        JPropertyDotNet: DotNet JProperty;
        JTokenDotNet: DotNet JToken;
    begin
        Clear(value);
        if JsonObjectDotNet.TryGetValue(propertyName, JTokenDotNet) then begin
            JPropertyDotNet := JsonObjectDotNet.Property(propertyName);
            value := JPropertyDotNet.Value;
            exit(true);
        end;
        exit(false);
    end;

    procedure GetStringPropertyValueFromJObjectByName(propertyName: Text; var value: Text): Boolean
    var
        VariantValue: Variant;
    begin
        Clear(value);
        if GetPropertyValueFromJObjectByName(propertyName, VariantValue) then begin
            value := Format(VariantValue);
            exit(true);
        end;
        exit(false);
    end;

    procedure GetEnumPropertyValueFromJObjectByName(propertyName: Text; var value: Option): Boolean
    var
        StringValue: Text;
    begin
        if GetStringPropertyValueFromJObjectByName(propertyName, StringValue) then begin
            Evaluate(value, StringValue, 0);
            exit(true);
        end;
        exit(false);
    end;

    procedure GetBoolPropertyValueFromJObjectByName(propertyName: Text; var value: Boolean): Boolean
    var
        StringValue: Text;
    begin
        if GetStringPropertyValueFromJObjectByName(propertyName, StringValue) then begin
            Evaluate(value, StringValue, 2);
            exit(true);
        end;
        exit(false);
    end;

    procedure GetDecimalPropertyValueFromJObjectByName(propertyName: Text; var value: Decimal): Boolean
    var
        StringValue: Text;
    begin
        if GetStringPropertyValueFromJObjectByName(propertyName, StringValue) then begin
            Evaluate(value, StringValue);
            exit(true);
        end;
        exit(false);
    end;

    procedure GetIntegerPropertyValueFromJObjectByName(propertyName: Text; var value: Integer): Boolean
    var
        StringValue: Text;
    begin
        if GetStringPropertyValueFromJObjectByName(propertyName, StringValue) then begin
            Evaluate(value, StringValue);
            exit(true);
        end;
        exit(false);
    end;

    procedure GetGuidPropertyValueFromJObjectByName(propertyName: Text; var value: Guid): Boolean
    var
        StringValue: Text;
    begin
        if GetStringPropertyValueFromJObjectByName(propertyName, StringValue) then begin
            Evaluate(value, StringValue);
            exit(true);
        end;
        exit(false);
    end;

    procedure ReplaceOrAddJPropertyInJObject(propertyName: Text; value: Variant): Boolean
    var
        JPropertyDotNet: DotNet JProperty;
        OldPropertyDotNet: DotNet JProperty;
        OldValue: Variant;
    begin
        JPropertyDotNet := JsonObjectDotNet.Property(propertyName);
        if not IsNull(JPropertyDotNet) then begin
            OldPropertyDotNet := JsonObjectDotNet.Property(propertyName);
            OldValue := OldPropertyDotNet.Value;
            JPropertyDotNet.Replace(JPropertyDotNet.JProperty(propertyName, value));
            exit(Format(OldValue) <> Format(value));
        end;

        AddJPropertyToJObject(propertyName, value);
        exit(true);
    end;

    procedure AddJObjectToCollection(JSONString: Text): Boolean
    begin
        if JSONString <> '' then
            JsonObjectDotNet := JsonObjectDotNet.Parse(JSONString)
        else
            InitializeEmptyObject();

        AddJObjectToCollection();
        exit(true);
    end;

    procedure RemoveJObjectFromCollection(Index: Integer): Boolean
    begin
        if (GetCollectionCount() = 0) or (GetCollectionCount() <= Index) then
            exit(false);

        JsonArrayDotNet.RemoveAt(Index);
        exit(true);
    end;

    procedure ReplaceJObjectInCollection(Index: Integer; JSONString: Text): Boolean
    begin
        if not GetJObjectFromCollectionByIndex(Index) then
            exit(false);

        if JSONString <> '' then
            JsonObjectDotNet := JsonObjectDotNet.Parse(JSONString)
        else
            InitializeEmptyObject();

        JsonArrayDotNet.RemoveAt(Index);
        JsonArrayDotNet.Insert(Index, JsonObjectDotNet);
        exit(true);
    end;

    local procedure GetJObjectFromCollectionByIndex(Index: Integer): Boolean
    begin
        if (GetCollectionCount() = 0) or (GetCollectionCount() <= Index) then
            exit(false);

        JsonObjectDotNet := JsonArrayDotNet.Item(Index);
        exit(not IsNull(JsonObjectDotNet))
    end;

    local procedure GetPropertyValueFromJObjectByPathSetToFieldRef(propertyPath: Text; var FieldRef: FieldRef): Boolean
    var
        RecID: RecordId;
        Value: Variant;
        IntVar: Integer;
        DecimalVal: Decimal;
        GuidVal: Guid;
        DateVal: Date;
        BoolVal, Success : Boolean;
        JPropertyDotNet: DotNet JProperty;
    begin
        Success := false;
        JPropertyDotNet := JsonObjectDotNet.SelectToken(propertyPath);

        if IsNull(JPropertyDotNet) then
            exit(false);

        Value := Format(JPropertyDotNet.Value, 0, 9);

        case FieldRef.Type of
            FieldType::Integer,
            FieldType::Decimal:
                begin
                    Success := Evaluate(DecimalVal, Value, 9);
                    FieldRef.Value(DecimalVal);
                end;
            FieldType::Date:
                begin
                    Success := Evaluate(DateVal, Value, 9);
                    FieldRef.Value(DateVal);
                end;
            FieldType::Boolean:
                begin
                    Success := Evaluate(BoolVal, Value, 9);
                    FieldRef.Value(BoolVal);
                end;
            FieldType::GUID:
                begin
                    Success := Evaluate(GuidVal, Value);
                    FieldRef.Value(GuidVal);
                end;
            FieldType::Text,
            FieldType::Code:
                begin
                    FieldRef.Value(CopyStr(Value, 1, FieldRef.Length));
                    Success := true;
                end;
            FieldType::Option:
                begin
                    if not Evaluate(IntVar, Value) then
                        IntVar := TextToOptionValue(Value, FieldRef.OptionCaption);
                    if IntVar >= 0 then begin
                        FieldRef.Value := IntVar;
                        Success := true;
                    end;
                end;
            FieldType::BLOB:
                if TryReadAsBase64(FieldRef, Value) then
                    Success := true;
            FieldType::RecordID:
                begin
                    Success := Evaluate(RecID, Value);
                    FieldRef.Value(RecID);
                end;
        end;

        exit(Success);
    end;

    [TryFunction]
    local procedure TryReadAsBase64(var BlobFieldRef: FieldRef; Value: Text)
    var
        Base64Convert: Codeunit "Base64 Convert";
        TempBlob: Codeunit "Temp Blob";
        RecordRef: RecordRef;
        OutStream: OutStream;
    begin
        TempBlob.CreateOutStream(OutStream);
        Base64Convert.FromBase64(Value, OutStream);
        RecordRef := BlobFieldRef.Record();
        TempBlob.ToRecordRef(RecordRef, BlobFieldRef.Number);
    end;

    local procedure TextToOptionValue(InputText: Text; OptionString: Text): Integer
    var
        IntVar: Integer;
        Counter: Integer;
    begin
        if InputText = '' then
            InputText := ' ';

        if Evaluate(IntVar, InputText) then begin
            if IntVar < 0 then
                IntVar := -1;
            if GetOptionsQuantity(OptionString) < IntVar then
                IntVar := -1;
        end else begin
            IntVar := -1;
            for Counter := 1 to GetOptionsQuantity(OptionString) + 1 do
                if UpperCase(GetSubStrByNo(Counter, OptionString)) = UpperCase(InputText) then
                    IntVar := Counter - 1;
        end;

        exit(IntVar);
    end;

    local procedure GetOptionsQuantity(OptionString: Text): Integer
    var
        Counter: Integer;
        CommaPosition: Integer;
    begin
        if StrPos(OptionString, ',') = 0 then
            exit(0);

        repeat
            CommaPosition := StrPos(OptionString, ',');
            OptionString := DelStr(OptionString, 1, CommaPosition);
            Counter := Counter + 1;
        until CommaPosition = 0;

        exit(Counter - 1);
    end;

    local procedure GetSubStrByNo(Number: Integer; CommaString: Text) SelectedStr: Text
    var
        SubStrQuantity: Integer;
        Counter: Integer;
        CommaPosition: Integer;
    begin
        if Number <= 0 then
            exit;

        SubStrQuantity := GetOptionsQuantity(CommaString);
        if SubStrQuantity + 1 < Number then
            exit;

        repeat
            Counter := Counter + 1;
            CommaPosition := StrPos(CommaString, ',');
            if CommaPosition = 0 then
                SelectedStr := CommaString
            else begin
                SelectedStr := CopyStr(CommaString, 1, CommaPosition - 1);
                CommaString := DelStr(CommaString, 1, CommaPosition);
            end;
        until Counter = Number;
    end;

    local procedure AddJPropertyToJObject(propertyName: Text; value: Variant)
    var
        JObjectDotNet: DotNet JObject;
        JPropertyDotNet: DotNet JProperty;
        ValueText: Text;
    begin
        case true of
            value.IsDotNet:
                begin
                    JObjectDotNet := value;
                    JsonObjectDotNet.Add(propertyName, JObjectDotNet);
                end;
            value.IsInteger,
            value.IsDecimal,
            value.IsBoolean:
                begin
                    JPropertyDotNet := JPropertyDotNet.JProperty(propertyName, value);
                    JsonObjectDotNet.Add(JPropertyDotNet);
                end;
            else begin
                ValueText := Format(value, 0, 9);
                JPropertyDotNet := JPropertyDotNet.JProperty(propertyName, ValueText);
                JsonObjectDotNet.Add(JPropertyDotNet);
            end;
        end;
    end;

    local procedure AddJObjectToCollection()
    begin
        JsonArrayDotNet.Add(JsonObjectDotNet.DeepClone());
    end;

    local procedure InitializeEmptyCollection()
    begin
        JsonArrayDotNet := JsonArrayDotNet.JArray();
    end;

    local procedure InitializeEmptyObject()
    begin
        JsonObjectDotNet := JsonObjectDotNet.JObject();
    end;
}