Codeunit 5459 JSON Management

App
Base Application
Namespace
System.Text
Versions
17-28

Procedures, 59Events, 1

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/JSONManagement.Codeunit.al754 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;

using Microsoft.CRM.Outlook;
using System;
using System.Utilities;
using System.Xml;

codeunit 5459 "JSON Management"
{

    trigger OnRun()
    begin
    end;

    var
        JsonArray: DotNet JArray;
        JsonObject: DotNet JObject;
        IEnumerator: DotNet GenericIEnumerator1;

    procedure InitializeCollection(JSONString: Text)
    begin
        InitializeCollectionFromString(JSONString);
    end;

    procedure InitializeEmptyCollection()
    begin
        JsonArray := JsonArray.JArray();
    end;

    procedure InitializeObject(JSONString: Text)
    begin
        InitializeObjectFromString(JSONString);
    end;

    [Scope('OnPrem')]
    procedure InitializeObjectFromJObject(NewJsonObject: DotNet JObject)
    begin
        JsonObject := NewJsonObject;
    end;

    [Scope('OnPrem')]
    procedure InitializeCollectionFromJArray(NewJsonArray: DotNet JArray)
    begin
        JsonArray := NewJsonArray;
    end;

    procedure InitializeEmptyObject()
    begin
        JsonObject := JsonObject.JObject();
    end;

    local procedure InitializeCollectionFromString(JSONString: Text)
    begin
        Clear(JsonArray);
        if JSONString <> '' then
            JsonArray := JsonArray.Parse(JSONString)
        else
            InitializeEmptyCollection();
    end;

    local procedure InitializeObjectFromString(JSONString: Text)
    begin
        Clear(JsonObject);
        if JSONString <> '' then
            JsonObject := JsonObject.Parse(JSONString)
        else
            InitializeEmptyObject();
    end;

    procedure InitializeFromString(JSONString: Text): Boolean
    begin
        Clear(JsonObject);
        if JSONString <> '' then
            exit(TryParseJObjectFromString(JsonObject, JSONString));

        InitializeEmptyObject();
        exit(true);
    end;

    [Scope('OnPrem')]
    procedure GetJSONObject(var JObject: DotNet JObject)
    begin
        JObject := JsonObject;
    end;

    [Scope('OnPrem')]
    procedure GetJsonArray(var JArray: DotNet JArray)
    begin
        JArray := JsonArray;
    end;

    procedure GetObjectFromCollectionByIndex(var "Object": Text; Index: Integer): Boolean
    var
        JObject: DotNet JObject;
    begin
        if not GetJObjectFromCollectionByIndex(JObject, Index) then
            exit(false);

        Object := JObject.ToString();
        exit(true);
    end;

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

        JObject := JsonArray.Item(Index);
        exit(not IsNull(JObject))
    end;

    [Scope('OnPrem')]
    procedure GetJObjectFromCollectionByPropertyValue(var JObject: DotNet JObject; propertyName: Text; value: Text): Boolean
    var
        IEnumerable: DotNet GenericIEnumerable1;
        IEnumerator: DotNet GenericIEnumerator1;
    begin
        Clear(JObject);
        IEnumerable := JsonArray.SelectTokens(StrSubstNo('$[?(@.%1 == ''%2'')]', propertyName, value), false);
        IEnumerator := IEnumerable.GetEnumerator();

        if IEnumerator.MoveNext() then begin
            JObject := IEnumerator.Current;
            exit(true);
        end;
    end;

    [Scope('OnPrem')]
    procedure GetPropertyValueFromJObjectByName(JObject: DotNet JObject; propertyName: Text; var value: Variant): Boolean
    var
        JProperty: DotNet JProperty;
        JToken: DotNet JToken;
    begin
        Clear(value);
        if JObject.TryGetValue(propertyName, JToken) then begin
            JProperty := JObject.Property(propertyName);
            value := JProperty.Value();
            exit(true);
        end;
    end;

    procedure GetPropertyValueByName(propertyName: Text; var value: Variant): Boolean
    begin
        exit(GetPropertyValueFromJObjectByName(JsonObject, propertyName, value));
    end;

    [Scope('OnPrem')]
    procedure GetPropertyValueFromJObjectByPathSetToFieldRef(JObject: DotNet JObject; propertyPath: Text; var FieldRef: FieldRef): Boolean
    var
        OutlookSynchTypeConv: Codeunit "Outlook Synch. Type Conv";
        JProperty: DotNet JProperty;
        RecID: RecordID;
        Value: Variant;
        DecimalVal: Decimal;
        BoolVal: Boolean;
        GuidVal: Guid;
        DateVal: Date;
        Success: Boolean;
        IntVar: Integer;
    begin
        Success := false;
        JProperty := JObject.SelectToken(propertyPath);

        if IsNull(JProperty) then
            exit(false);

        Value := Format(JProperty.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 := OutlookSynchTypeConv.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;

    procedure GetPropertyValueFromJObjectByPath(JObject: DotNet JObject; fullyQualifiedPropertyName: Text; var value: Variant): Boolean
    var
        containerJObject: DotNet JObject;
        propertyName: Text;
    begin
        Clear(value);
        DecomposeQualifiedPathToContainerObjectAndPropertyName(JObject, fullyQualifiedPropertyName, containerJObject, propertyName);
        if IsNull(containerJObject) then
            exit(false);

        exit(GetPropertyValueFromJObjectByName(containerJObject, propertyName, value));
    end;

    [Scope('OnPrem')]
    procedure GetStringPropertyValueFromJObjectByName(JObject: DotNet JObject; propertyName: Text; var value: Text): Boolean
    var
        VariantValue: Variant;
    begin
        Clear(value);
        if GetPropertyValueFromJObjectByName(JObject, propertyName, VariantValue) then begin
            value := Format(VariantValue);
            exit(true);
        end;
        exit(false);
    end;

    procedure GetStringPropertyValueByName(propertyName: Text; var value: Text): Boolean
    begin
        exit(GetStringPropertyValueFromJObjectByName(JsonObject, propertyName, value));
    end;

    [Scope('OnPrem')]
    procedure GetStringPropertyValueFromJObjectByPath(JObject: DotNet JObject; fullyQualifiedPropertyName: Text; var value: Text): Boolean
    var
        VariantValue: Variant;
    begin
        Clear(value);
        if GetPropertyValueFromJObjectByPath(JObject, fullyQualifiedPropertyName, VariantValue) then begin
            value := Format(VariantValue);
            exit(true);
        end;
        exit(false);
    end;

    [Scope('OnPrem')]
    procedure GetEnumPropertyValueFromJObjectByName(JObject: DotNet JObject; propertyName: Text; var value: Option)
    var
        StringValue: Text;
    begin
        GetStringPropertyValueFromJObjectByName(JObject, propertyName, StringValue);
        Evaluate(value, StringValue, 0);
    end;

    [Scope('OnPrem')]
    procedure GetBoolPropertyValueFromJObjectByName(JObject: DotNet JObject; propertyName: Text; var value: Boolean): Boolean
    var
        StringValue: Text;
    begin
        if GetStringPropertyValueFromJObjectByName(JObject, propertyName, StringValue) then begin
            Evaluate(value, StringValue, 2);
            exit(true);
        end;
        exit(false);
    end;

    [Scope('OnPrem')]
    procedure GetArrayPropertyValueFromJObjectByName(JObject: DotNet JObject; propertyName: Text; var JArray: DotNet JArray): Boolean
    var
        JProperty: DotNet JProperty;
        JToken: DotNet JToken;
    begin
        Clear(JArray);
        if JObject.TryGetValue(propertyName, JToken) then begin
            JProperty := JObject.Property(propertyName);
            JArray := JProperty.Value();
            exit(true);
        end;
        exit(false);
    end;

    procedure GetArrayPropertyValueAsStringByName(propertyName: Text; var value: Text): Boolean
    var
        JArray: DotNet JArray;
    begin
        if not GetArrayPropertyValueFromJObjectByName(JsonObject, propertyName, JArray) then
            exit(false);

        value := JArray.ToString();
        exit(true);
    end;

    [Scope('OnPrem')]
    procedure GetObjectPropertyValueFromJObjectByName(JObject: DotNet JObject; propertyName: Text; var JSubObject: DotNet JObject): Boolean
    var
        JProperty: DotNet JProperty;
        JToken: DotNet JToken;
    begin
        Clear(JSubObject);
        if JObject.TryGetValue(propertyName, JToken) then begin
            JProperty := JObject.Property(propertyName);
            JSubObject := JProperty.Value();
            exit(true);
        end;
        exit(false);
    end;

    [Scope('OnPrem')]
    procedure GetDecimalPropertyValueFromJObjectByName(JObject: DotNet JObject; propertyName: Text; var value: Decimal): Boolean
    var
        StringValue: Text;
    begin
        if GetStringPropertyValueFromJObjectByName(JObject, propertyName, StringValue) then begin
            Evaluate(value, StringValue);
            exit(true);
        end;
        exit(false);
    end;

    [Scope('OnPrem')]
    procedure GetGuidPropertyValueFromJObjectByName(JObject: DotNet JObject; propertyName: Text; var value: Guid): Boolean
    var
        StringValue: Text;
    begin
        if GetStringPropertyValueFromJObjectByName(JObject, propertyName, StringValue) then begin
            Evaluate(value, StringValue);
            exit(true);
        end;
        exit(false);
    end;

    local procedure GetValueFromJObject(JToken: DotNet JToken; var value: Variant)
    var
        JValue: DotNet JValue;
    begin
        Clear(value);
        JValue := JToken;
        value := JValue.Value();
    end;

    [Scope('OnPrem')]
    procedure GetStringValueFromJObject(JObject: DotNet JObject; var value: Text)
    var
        VariantValue: Variant;
    begin
        Clear(value);
        GetValueFromJObject(JObject, VariantValue);
        value := Format(VariantValue);
    end;

    [Scope('OnPrem')]
    procedure AddJArrayToJObject(var JObject: DotNet JObject; propertyName: Text; value: Variant)
    var
        JArray2: DotNet JArray;
        JProperty: DotNet JProperty;
    begin
        JArray2 := value;
        JObject.Add(JProperty.JProperty(propertyName, JArray2));
    end;

    [Scope('OnPrem')]
    procedure AddJObjectToJObject(var JObject: DotNet JObject; propertyName: Text; value: Variant)
    var
        JObject2: DotNet JObject;
        JToken: DotNet JToken;
        ValueText: Text;
    begin
        JObject2 := value;
        ValueText := Format(value);
        JObject.Add(propertyName, JToken.Parse(ValueText));
    end;

    [Scope('OnPrem')]
    procedure AddJObjectToJArray(var JArray: DotNet JArray; value: Variant)
    var
        JObject: DotNet JObject;
    begin
        JObject := value;
        JArray.Add(JObject.DeepClone());
    end;

    [Scope('OnPrem')]
    procedure AddJPropertyToJObject(var JObject: DotNet JObject; propertyName: Text; value: Variant)
    var
        JObject2: DotNet JObject;
        JProperty: DotNet JProperty;
        ValueText: Text;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeAddJPropertyToJObject(JObject, JProperty, propertyName, value, IsHandled);
        if IsHandled then
            exit;

        case true of
            value.IsDotNet:
                begin
                    JObject2 := value;
                    JObject.Add(propertyName, JObject2);
                end;
            value.IsInteger,
            value.IsDecimal,
            value.IsBoolean:
                begin
                    JProperty := JProperty.JProperty(propertyName, value);
                    JObject.Add(JProperty);
                end;
            else begin
                ValueText := Format(value, 0, 9);
                JProperty := JProperty.JProperty(propertyName, ValueText);
                JObject.Add(JProperty);
            end;
        end;
    end;

    [Scope('OnPrem')]
    procedure AddNullJPropertyToJObject(var JObject: DotNet JObject; propertyName: Text)
    var
        JValue: DotNet JValue;
    begin
        JObject.Add(propertyName, JValue.CreateNull());
    end;

    [Scope('OnPrem')]
    procedure AddJValueToJObject(var JObject: DotNet JToken; value: Variant)
    var
        JValue: DotNet JValue;
    begin
        JObject := JValue.JValue(value);
    end;

    [Scope('OnPrem')]
    procedure AddJObjectToCollection(JObject: DotNet JObject)
    begin
        JsonArray.Add(JObject.DeepClone());
    end;

    [Scope('OnPrem')]
    procedure AddJArrayContentToCollection(JArray: DotNet JArray)
    begin
        JsonArray.Merge(JArray.DeepClone());
    end;

    [Scope('OnPrem')]
    procedure ReplaceOrAddJPropertyInJObject(var JObject: DotNet JObject; propertyName: Text; value: Variant): Boolean
    var
        JProperty: DotNet JProperty;
        OldProperty: DotNet JProperty;
        oldValue: Variant;
    begin
        JProperty := JObject.Property(propertyName);
        if not IsNull(JProperty) then begin
            OldProperty := JObject.Property(propertyName);
            oldValue := OldProperty.Value();
            JProperty.Replace(JProperty.JProperty(propertyName, value));
            exit(Format(oldValue) <> Format(value));
        end;

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

    [Scope('OnPrem')]
    procedure ReplaceOrAddDescendantJPropertyInJObject(var JObject: DotNet JObject; fullyQualifiedPropertyName: Text; value: Variant): Boolean
    var
        containerJObject: DotNet JObject;
        propertyName: Text;
    begin
        DecomposeQualifiedPathToContainerObjectAndPropertyName(JObject, fullyQualifiedPropertyName, containerJObject, propertyName);
        exit(ReplaceOrAddJPropertyInJObject(containerJObject, propertyName, value));
    end;

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

    procedure WriteCollectionToString(): Text
    begin
        exit(JsonArray.ToString());
    end;

    procedure WriteObjectToString(): Text
    begin
        if not IsNull(JsonObject) then
            exit(JsonObject.ToString());
    end;

    procedure FormatDecimalToJSONProperty(Value: Decimal; PropertyName: Text): Text
    var
        JProperty: DotNet JProperty;
    begin
        JProperty := JProperty.JProperty(PropertyName, Value);
        exit(JProperty.ToString());
    end;

    local procedure GetLastIndexOfPeriod(String: Text) LastIndex: Integer
    var
        Index: Integer;
    begin
        Index := StrPos(String, '.');
        LastIndex := Index;
        while Index > 0 do begin
            String := CopyStr(String, Index + 1);
            Index := StrPos(String, '.');
            LastIndex += Index;
        end;
    end;

    local procedure GetSubstringToLastPeriod(String: Text): Text
    var
        Index: Integer;
    begin
        Index := GetLastIndexOfPeriod(String);
        if Index > 0 then
            exit(CopyStr(String, 1, Index - 1));
    end;

    local procedure DecomposeQualifiedPathToContainerObjectAndPropertyName(var JObject: DotNet JObject; fullyQualifiedPropertyName: Text; var containerJObject: DotNet JObject; var propertyName: Text)
    var
        containerJToken: DotNet JToken;
        containingPath: Text;
    begin
        Clear(containerJObject);
        propertyName := '';

        containingPath := GetSubstringToLastPeriod(fullyQualifiedPropertyName);
        containerJToken := JObject.SelectToken(containingPath);
        if IsNull(containerJToken) then begin
            DecomposeQualifiedPathToContainerObjectAndPropertyName(JObject, containingPath, containerJObject, propertyName);
            containerJObject.Add(propertyName, JObject.JObject());
            containerJToken := JObject.SelectToken(containingPath);
        end;

        containerJObject := containerJToken;
        if containingPath <> '' then
            propertyName := CopyStr(fullyQualifiedPropertyName, StrLen(containingPath) + 2)
        else
            propertyName := fullyQualifiedPropertyName;
    end;

    procedure XMLTextToJSONText(Xml: Text) Json: Text
    var
        XMLDOMMgt: Codeunit "XML DOM Management";
        JsonConvert: DotNet JsonConvert;
        JsonFormatting: DotNet Formatting;
        XmlDocument: DotNet XmlDocument;
    begin
        XMLDOMMgt.LoadXMLDocumentFromText(Xml, XmlDocument);
        Json := JsonConvert.SerializeXmlNode(XmlDocument.DocumentElement, JsonFormatting.Indented, true);
    end;

    procedure JSONTextToXMLText(Json: Text; DocumentElementName: Text) Xml: Text
    var
        JsonConvert: DotNet JsonConvert;
        XmlDocument: DotNet XmlDocument;
    begin
        XmlDocument := JsonConvert.DeserializeXmlNode(Json, DocumentElementName);
        Xml := XmlDocument.OuterXml();
    end;

    [TryFunction]
    [Scope('OnPrem')]
    procedure TryParseJObjectFromString(var JObject: DotNet JObject; StringToParse: Variant)
    begin
        JObject := JObject.Parse(Format(StringToParse));
    end;

    [TryFunction]
    [Scope('OnPrem')]
    procedure TryParseJArrayFromString(var JsonArray: DotNet JArray; StringToParse: Variant)
    begin
        JsonArray := JsonArray.Parse(Format(StringToParse));
    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;

    procedure SetValue(Path: Text; Value: Variant)
    begin
        if IsNull(JsonObject) then
            InitializeEmptyObject();
        ReplaceOrAddDescendantJPropertyInJObject(JsonObject, Path, Value);
    end;

    procedure GetValue(Path: Text): Text
    var
        SelectedJToken: DotNet JToken;
    begin
        if IsNull(JsonObject) then
            exit('');

        SelectedJToken := JsonObject.SelectToken(Path);
        if not IsNull(SelectedJToken) then
            exit(SelectedJToken.ToString());
    end;

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

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

    procedure HasValue(Name: Text; Value: Text): Boolean
    begin
        if not IsNull(JsonObject) then
            exit(StrPos(GetValue(Name), Value) = 1);
    end;

    procedure AddArrayValue(Value: Variant)
    begin
        if IsNull(JsonArray) then
            InitializeEmptyCollection();
        JsonArray.Add(Value);
    end;

    procedure AddJson(Path: Text; JsonString: Text)
    var
        JObject: DotNet JObject;
    begin
        if JsonString <> '' then
            if TryParseJObjectFromString(JObject, JsonString) then
                SetValue(Path, JObject);
    end;

    procedure AddJsonArray(Path: Text; JsonArrayString: Text)
    var
        JsonArrayLocal: DotNet JArray;
    begin
        if JsonArrayString <> '' then
            if TryParseJArrayFromString(JsonArrayLocal, JsonArrayString) then
                SetValue(Path, JsonArrayLocal);
    end;

    procedure SelectTokenFromRoot(Path: Text): Boolean
    begin
        if IsNull(JsonObject) then
            exit(false);

        JsonObject := JsonObject.Root;
        JsonObject := JsonObject.SelectToken(Path);
        exit(not IsNull(JsonObject));
    end;

    procedure ReadProperties(): Boolean
    var
        IEnumerable: DotNet GenericIEnumerable1;
    begin
        if not JsonObject.HasValues then
            exit(false);

        IEnumerable := JsonObject.Properties();
        IEnumerator := IEnumerable.GetEnumerator();
        exit(true);
    end;

    procedure GetNextProperty(var Name: Text; var Value: Text): Boolean
    var
        JProperty: DotNet JProperty;
    begin
        Name := '';
        Value := '';

        if not IEnumerator.MoveNext() then
            exit(false);

        JProperty := IEnumerator.Current;
        if IsNull(JProperty) then
            exit(false);

        Name := JProperty.Name;
        Value := Format(JProperty.Value);
        exit(true);
    end;

    procedure SelectItemFromRoot(Path: Text; Index: Integer): Boolean
    begin
        if SelectTokenFromRoot(Path) then
            JsonObject := JsonObject.Item(Index);
        exit(not IsNull(JsonObject));
    end;

    procedure GetCount(): Integer
    begin
        if not IsNull(JsonObject) then
            exit(JsonObject.Count);
    end;

    procedure SetJsonWebResponseError(var JsonString: Text; "code": Text; name: Text; description: Text)
    begin
        if InitializeFromString(JsonString) then begin
            SetValue('Error.code', code);
            SetValue('Error.name', name);
            SetValue('Error.description', description);
            JsonString := WriteObjectToString();
        end;
    end;

    procedure GetJsonWebResponseError(JsonString: Text; var "code": Text; var name: Text; var description: Text): Boolean
    begin
        if InitializeFromString(JsonString) then begin
            code := GetValue('Error.code');
            name := GetValue('Error.name');
            description := GetValue('Error.description');
            exit(true);
        end;

        exit(false);
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeAddJPropertyToJObject(var JObject: DotNet "JObject"; var JProperty: DotNet "JProperty"; propertyName: Text; value: Variant; var IsHandled: Boolean)
    begin
    end;
}