diff --git a/src/System Application/App/Json/README.md b/src/System Application/App/Json/README.md new file mode 100644 index 0000000000..28b5834ebb --- /dev/null +++ b/src/System Application/App/Json/README.md @@ -0,0 +1 @@ +Provides tools for working with JSON data such as reading, writing and parsing JSON. \ No newline at end of file diff --git a/src/System Application/App/Json/app.json b/src/System Application/App/Json/app.json new file mode 100644 index 0000000000..ef215d8ce3 --- /dev/null +++ b/src/System Application/App/Json/app.json @@ -0,0 +1,45 @@ +{ + "id": "645965f7-95bf-4ee9-bf97-84e45dc6c6d1", + "name": "Json", + "publisher": "Microsoft", + "brief": "Provides tools for working with JSON data.", + "description": "Provides tools for working with JSON data such as reading, writing and parsing JSON.", + "version": "25.0.0.0", + "privacyStatement": "https://go.microsoft.com/fwlink/?linkid=724009", + "EULA": "https://go.microsoft.com/fwlink/?linkid=2009120", + "help": "https://go.microsoft.com/fwlink/?linkid=2103698", + "url": "https://go.microsoft.com/fwlink/?linkid=724011", + "logo": "", + "dependencies": [ + { + "id": "e31ad830-3d46-472e-afeb-1d3d35247943", + "name": "BLOB Storage", + "publisher": "Microsoft", + "version": "25.0.0.0" + }, + { + "id": "0846d207-5dec-4c1b-afd8-6a25e1e14b9d", + "name": "Base64 Convert", + "publisher": "Microsoft", + "version": "25.0.0.0" + }, + { + "id": "7e3b999e-1182-45d2-8b82-d5127ddba9b2", + "name": "DotNet Aliases", + "publisher": "Microsoft", + "version": "25.0.0.0" + } + ], + "screenshots": [ + + ], + "platform": "24.0.0.0", + "idRanges": [ + { + "from": 5460, + "to": 5461 + } + ], + "target": "OnPrem", + "contextSensitiveHelpUrl": "https://learn.microsoft.com/dynamics365/business-central/" +} diff --git a/src/System Application/App/Json/src/Json.Codeunit.al b/src/System Application/App/Json/src/Json.Codeunit.al new file mode 100644 index 0000000000..bd72b4ac50 --- /dev/null +++ b/src/System Application/App/Json/src/Json.Codeunit.al @@ -0,0 +1,226 @@ +// ------------------------------------------------------------------------------------------------ +// 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; + +codeunit 5460 Json +{ + Access = Public; + InherentEntitlements = X; + InherentPermissions = X; + + var + JsonImpl: Codeunit "Json Impl."; + + /// + /// Initializes the JSON array with the specified JSON string. + /// + /// The Json string + procedure InitializeCollection(JSONString: Text) + begin + JsonImpl.InitializeCollectionFromString(JSONString); + end; + + /// + /// Initializes the JSON object with the specified JSON string. + /// + /// The Json string + procedure InitializeObject(JSONString: Text) + begin + JsonImpl.InitializeObjectFromString(JSONString); + end; + + /// + /// Returns the number of elements in the JSON array. + /// + /// The number of elements in the JSON array + procedure GetCollectionCount(): Integer + begin + exit(JsonImpl.GetCollectionCount()); + end; + + /// + /// Returns the JSON array in text format. + /// + /// The JSON array in text format + procedure GetCollectionAsText(): Text + begin + exit(JsonImpl.GetCollectionAsText()); + end; + + /// + /// Returns the JSON array. + /// + /// The JSON array + procedure GetCollection(): JsonArray + begin + exit(JsonImpl.GetCollection()); + end; + + /// + /// Returns the JSON object in text format. + /// + /// The JSON object in text format + procedure GetObjectAsText(): Text + begin + exit(JsonImpl.GetObjectAsText()); + end; + + /// + /// Returns the JSON object. + /// + /// The JSON object + procedure GetObject(): JsonObject + begin + exit(JsonImpl.GetObject()); + end; + + /// + /// Returns the JSON object at the specified index in the JSON array. + /// + /// The index of the JSON object + /// The JSON object in text format + /// True if the JSON object is returned; otherwise, false + procedure GetObjectFromCollectionByIndex(Index: Integer; var JsonObjectTxt: Text): Boolean + begin + exit(JsonImpl.GetObjectFromCollectionByIndex(Index, JsonObjectTxt)); + end; + + /// + /// Gets the value at the specified property path in the JSON object and sets it to the specified record field. + /// + /// The record reference + /// The property path + /// The field number + /// True if the value is set to the record field; otherwise, false + /// + /// Next type of fields are supported: Integer, Decimal, Date, Boolean, GUID, Text, Code, Option, BLOB, RecordID + /// Text values are trimmed to the Max Length of the field. + /// + procedure GetValueAndSetToRecFieldNo(RecordRef: RecordRef; PropertyPath: Text; FieldNo: Integer): Boolean + begin + exit(JsonImpl.GetValueAndSetToRecFieldNo(RecordRef, PropertyPath, FieldNo)); + end; + + /// + /// Gets the value at the specified property name in the JSON object. + /// + /// The property name + /// The value + /// True if the value is returned; otherwise, false + procedure GetPropertyValueByName(PropertyName: Text; var Value: Variant): Boolean + begin + exit(JsonImpl.GetPropertyValueFromJObjectByName(PropertyName, Value)); + end; + + /// + /// Gets the text value at the specified property name in the JSON object. + /// + /// The property name + /// The value + /// True if the value is returned; otherwise, false + procedure GetStringPropertyValueByName(PropertyName: Text; var Value: Text): Boolean + begin + exit(JsonImpl.GetStringPropertyValueFromJObjectByName(PropertyName, Value)); + end; + + /// + /// Gets the option value at the specified property name in the JSON object. + /// + /// The property name + /// The value + /// True if the value is returned; otherwise, false + procedure GetEnumPropertyValueFromJObjectByName(PropertyName: Text; var Value: Option): Boolean + begin + exit(JsonImpl.GetEnumPropertyValueFromJObjectByName(PropertyName, Value)); + end; + + /// + /// Gets the boolean value at the specified property name in the JSON object. + /// + /// The property name + /// The value + /// True if the value is returned; otherwise, false + procedure GetBoolPropertyValueFromJObjectByName(PropertyName: Text; var Value: Boolean): Boolean + begin + exit(JsonImpl.GetBoolPropertyValueFromJObjectByName(PropertyName, Value)); + end; + + /// + /// Gets the decimal value at the specified property name in the JSON object. + /// + /// The property name + /// The value + /// True if the value is returned; otherwise, false + procedure GetDecimalPropertyValueFromJObjectByName(PropertyName: Text; var Value: Decimal): Boolean + begin + exit(JsonImpl.GetDecimalPropertyValueFromJObjectByName(PropertyName, Value)); + end; + + /// + /// Gets the integer value at the specified property name in the JSON object. + /// + /// The property name + /// The value + /// True if the value is returned; otherwise, false + procedure GetIntegerPropertyValueFromJObjectByName(PropertyName: Text; var Value: Integer): Boolean + begin + exit(JsonImpl.GetIntegerPropertyValueFromJObjectByName(PropertyName, Value)); + end; + + /// + /// Gets the Guid value at the specified property name in the JSON object. + /// + /// The property name + /// The value + /// True if the value is returned; otherwise, false + procedure GetGuidPropertyValueFromJObjectByName(PropertyName: Text; var Value: Guid): Boolean + begin + exit(JsonImpl.GetGuidPropertyValueFromJObjectByName(PropertyName, Value)); + end; + + /// + /// Replace or add the specified property in the JSON object. + /// + /// The property name + /// The value + /// True if the property is replaced or added; otherwise, false + procedure ReplaceOrAddJPropertyInJObject(PropertyName: Text; Value: Variant): Boolean + begin + exit(JsonImpl.ReplaceOrAddJPropertyInJObject(PropertyName, Value)); + end; + + /// + /// Add the the JSON object to the JSON array. + /// + /// The JSON object in text format + /// True if the JSON object is added; otherwise, false + procedure AddJObjectToCollection(Value: Text): Boolean + begin + exit(JsonImpl.AddJObjectToCollection(Value)); + end; + + /// + /// Remove the JSON object at the specified index in the JSON array. + /// + /// The index of the JSON object + /// True if the JSON object is removed; otherwise, false + procedure RemoveJObjectFromCollection(Index: Integer): Boolean + begin + exit(JsonImpl.RemoveJObjectFromCollection(Index)); + end; + + /// + /// Replace the specified JSON object in the JSON array. + /// + /// The index of the JSON object + /// The JSON object in text format + /// True if the JSON object is replaced; otherwise, false + procedure ReplaceJObjectInCollection(Index: Integer; Value: Text): Boolean + begin + exit(JsonImpl.ReplaceJObjectInCollection(Index, Value)); + end; + +} diff --git a/src/System Application/App/Json/src/JsonImpl.Codeunit.al b/src/System Application/App/Json/src/JsonImpl.Codeunit.al new file mode 100644 index 0000000000..5516ed4cc2 --- /dev/null +++ b/src/System Application/App/Json/src/JsonImpl.Codeunit.al @@ -0,0 +1,417 @@ +// ------------------------------------------------------------------------------------------------ +// 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 + JsonArrayDotNet: DotNet JArray; + JsonObjectDotNet: DotNet JObject; + + procedure InitializeCollectionFromString(JSONString: Text) + begin + Clear(JsonArrayDotNet); + if JSONString <> '' then + JsonArrayDotNet := JsonArrayDotNet.Parse(JSONString) + else + InitializeEmptyCollection(); + end; + + procedure InitializeObjectFromString(JSONString: Text) + begin + Clear(JsonObjectDotNet); + if JSONString <> '' then + JsonObjectDotNet := JsonObjectDotNet.Parse(JSONString) + else + InitializeEmptyObject(); + end; + + procedure GetCollectionCount(): Integer + begin + exit(JsonArrayDotNet.Count); + end; + + procedure GetCollectionAsText() Value: Text + begin + GetCollection().WriteTo(Value); + 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; +} \ No newline at end of file diff --git a/src/System Application/Test/Json/app.json b/src/System Application/Test/Json/app.json new file mode 100644 index 0000000000..d5a63d7d46 --- /dev/null +++ b/src/System Application/Test/Json/app.json @@ -0,0 +1,35 @@ +{ + "id": "4b948e84-8ef6-4317-95c1-ad9e29f02c79", + "name": "Json Test", + "publisher": "Microsoft", + "brief": "", + "description": "Tests for the Json module", + "version": "25.0.0.0", + "privacyStatement": "", + "EULA": "", + "help": "", + "url": "", + "logo": "", + "dependencies": [ + { + "id": "645965f7-95bf-4ee9-bf97-84e45dc6c6d1", + "name": "Json", + "publisher": "Microsoft", + "version": "25.0.0.0" + }, + { + "id": "dd0be2ea-f733-4d65-bb34-a28f4624fb14", + "name": "Library Assert", + "publisher": "Microsoft", + "version": "25.0.0.0" + } + ], + "platform": "24.0.0.0", + "target": "OnPrem", + "idRanges": [ + { + "from": 139910, + "to": 139910 + } + ] +} diff --git a/src/System Application/Test/Json/src/JsonTest.Codeunit.al b/src/System Application/Test/Json/src/JsonTest.Codeunit.al new file mode 100644 index 0000000000..a2854e12f9 --- /dev/null +++ b/src/System Application/Test/Json/src/JsonTest.Codeunit.al @@ -0,0 +1,376 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.Test.Text.Json; + +using System.Text.Json; +using System.Device; +using System.TestLibraries.Utilities; + +codeunit 139910 "Json Test" +{ + Subtype = Test; + + var + Assert: Codeunit "Library Assert"; + + [Test] + procedure TestGetCollectionCount() + var + Json: Codeunit "Json"; + ExpectedCount: Integer; + ActualCount: Integer; + begin + // [GIVEN] A JSON collection is initialized with a known number of elements + Json.InitializeCollection('[{"id":"ABC123"},{"id":"XYZ789"}]'); + ExpectedCount := 2; + + // [WHEN] Retrieve the count of elements in the collection + ActualCount := Json.GetCollectionCount(); + + // [THEN] The actual count matches the expected count + Assert.AreEqual(ExpectedCount, ActualCount, 'The count of elements in the JSON collection does not match the expected value.'); + end; + + [Test] + procedure TestGetObjectFromCollectionByIndex() + var + Json: Codeunit "Json"; + ExpectedJObject: JsonObject; + ExpectedJObjectText: Text; + ActualJObject: JsonObject; + ActualJObjectText: Text; + Success: Boolean; + begin + // [GIVEN] A JSON collection with known objects + Json.InitializeCollection('[{"id":"ABC123"},{"id":"XYZ789"}]'); + + // [WHEN] Retrieve an object by its index + ExpectedJObject.ReadFrom('{"id":"XYZ789"}'); + ExpectedJObject.WriteTo(ExpectedJObjectText); + Success := Json.GetObjectFromCollectionByIndex(1, ActualJObjectText); // Index is zero-based + ActualJObject.ReadFrom(ActualJObjectText); + ActualJObject.WriteTo(ActualJObjectText); + + // [THEN] The retrieved object matches the expected object + Assert.IsTrue(Success, 'Failed to retrieve object by index.'); + Assert.AreEqual(ExpectedJObjectText, ActualJObjectText, 'The retrieved object does not match the expected object.'); + end; + + [Test] + procedure TestGetObjectFromCollectionByZeroIndex() + var + Json: Codeunit "Json"; + ExpectedJObject: JsonObject; + ExpectedJObjectText: Text; + ActualJObject: JsonObject; + ActualJObjectText: Text; + Success: Boolean; + begin + // [GIVEN] A JSON collection with known objects + Json.InitializeCollection('[{"id":"ABC123"},{"id":"XYZ789"}]'); + + // [WHEN] Retrieve an object by a zero index + ExpectedJObject.ReadFrom('{"id":"ABC123"}'); + ExpectedJObject.WriteTo(ExpectedJObjectText); + Success := Json.GetObjectFromCollectionByIndex(0, ActualJObjectText); + ActualJObject.ReadFrom(ActualJObjectText); + ActualJObject.WriteTo(ActualJObjectText); + + // [THEN] The retrieved object matches the expected object + Assert.IsTrue(Success, 'Failed to retrieve object by index.'); + Assert.AreEqual(ExpectedJObjectText, ActualJObjectText, 'The retrieved object does not match the expected object.'); + end; + + [Test] + procedure TestGetValueAndSetToRecFieldNo() + var + Printer: Record Printer; + Json: Codeunit "Json"; + RecRef: RecordRef; + JsonObjectText: Text; + begin + // [GIVEN] A JSON object and a record initialized + JsonObjectText := '{"id":"ABC123","name":"Test Name"}'; + Json.InitializeObject(JsonObjectText); + RecRef.GetTable(Printer); + + // [WHEN] Set values from JSON to record fields + Json.GetValueAndSetToRecFieldNo(RecRef, 'id', Printer.FieldNo(ID)); + Json.GetValueAndSetToRecFieldNo(RecRef, 'name', Printer.FieldNo(Name)); + RecRef.SetTable(Printer); + + // [THEN] The record fields are updated correctly + Assert.AreEqual('ABC123', Printer.ID, 'The Id field was not set correctly.'); + Assert.AreEqual('Test Name', Printer.Name, 'The Name field was not set correctly.'); + end; + + [Test] + procedure TestGetPropertyValueByName() + var + Json: Codeunit "Json"; + JsonObjectText: Text; + Value: Variant; + begin + // [GIVEN] A JSON object with a known value + JsonObjectText := '{"id":"ABC123", "name":"Test Name"}'; + Json.InitializeObject(JsonObjectText); + + // [WHEN] Retrieve a value from the JSON object + Json.GetPropertyValueByName('id', Value); + + // [THEN] The retrieved value matches the expected value + Assert.AreEqual('ABC123', Format(Value), 'The retrieved value does not match the expected value.'); + end; + + [Test] + procedure TestGetStringPropertyValueByName() + var + Json: Codeunit "Json"; + JsonObjectText: Text; + Value: Text; + begin + // [GIVEN] A JSON object with a known value + JsonObjectText := '{"id":"ABC123", "name":"Test Name"}'; + Json.InitializeObject(JsonObjectText); + + // [WHEN] Retrieve a value from the JSON object + Json.GetStringPropertyValueByName('id', Value); + + // [THEN] The retrieved value matches the expected value + Assert.AreEqual('ABC123', Value, 'The retrieved value does not match the expected value.'); + + // [WHEN] Retrieve a value from the JSON object + Json.GetStringPropertyValueByName('name', Value); + + // [THEN] The retrieved value matches the expected value + Assert.AreEqual('Test Name', Value, 'The retrieved value does not match the expected value.'); + end; + + [Test] + procedure TestGetIntegerPropertyValueFromJObjectByName() + var + Json: Codeunit "Json"; + JsonObjectText: Text; + Value: Integer; + begin + // [GIVEN] A JSON object with a known value + JsonObjectText := '{"id":123, "name":"Test Name"}'; + Json.InitializeObject(JsonObjectText); + + // [WHEN] Retrieve a value from the JSON object + Json.GetIntegerPropertyValueFromJObjectByName('id', Value); + + // [THEN] The retrieved value matches the expected value + Assert.AreEqual(123, Value, 'The retrieved value does not match the expected value.'); + end; + + [Test] + procedure TestGetBoolPropertyValueFromJObjectByName() + var + Json: Codeunit "Json"; + JsonObjectText: Text; + Value: Boolean; + begin + // [GIVEN] A JSON object with a known value + JsonObjectText := '{"id":123, "name":"Test Name", "isActive":true}'; + Json.InitializeObject(JsonObjectText); + + // [WHEN] Retrieve a value from the JSON object + Json.GetBoolPropertyValueFromJObjectByName('isActive', Value); + + // [THEN] The retrieved value matches the expected value + Assert.IsTrue(Value, 'The retrieved value does not match the expected value.'); + end; + + [Test] + procedure TestGetDecimalPropertyValueFromJObjectByName() + var + Json: Codeunit "Json"; + JsonObjectText: Text; + Value: Decimal; + begin + // [GIVEN] A JSON object with a known value + JsonObjectText := '{"id":123, "name":"Test Name", "price":123.45}'; + Json.InitializeObject(JsonObjectText); + + // [WHEN] Retrieve a value from the JSON object + Json.GetDecimalPropertyValueFromJObjectByName('price', Value); + + // [THEN] The retrieved value matches the expected value + Assert.AreEqual(123.45, Value, 'The retrieved value does not match the expected value.'); + end; + + [Test] + procedure TestGetEnumPropertyValueFromJObjectByName() + var + Json: Codeunit "Json"; + JsonObjectText: Text; + Value: Option Option1,Option2,Option3; + begin + // [GIVEN] A JSON object with a known value + JsonObjectText := '{"id":123, "name":"Test Name", "optionValue":"Option1"}'; + Json.InitializeObject(JsonObjectText); + + // [WHEN] Retrieve a value from the JSON object + Json.GetEnumPropertyValueFromJObjectByName('optionValue', Value); + + // [THEN] The retrieved value matches the expected value + Assert.AreEqual(Value::Option1, Value, 'The retrieved value does not match the expected value.'); + end; + + [Test] + procedure TestGetCollectionAsText() + var + Json: Codeunit "Json"; + JsonArrayText: Text; + begin + + // [GIVEN] A JSON array with a known value + Json.InitializeCollection('[{"id":"ABC123"},{"id":"XYZ789"}]'); + + // [WHEN] Retrieve JSON array + JsonArrayText := Json.GetCollectionAsText(); + + // [THEN] The retrieved value matches the expected value + Assert.AreEqual('[{"id":"ABC123"},{"id":"XYZ789"}]', JsonArrayText, 'The retrieved value does not match the expected value.'); + end; + + [Test] + procedure TestGetCollection() + var + Json: Codeunit "Json"; + JsonArray: JsonArray; + JsonArrayText: Text; + begin + // [GIVEN] A JSON array with a known value + Json.InitializeCollection('[{"id":"ABC123"},{"id":"XYZ789"}]'); + + // [WHEN] Retrieve JSON array + JsonArray := Json.GetCollection(); + JsonArray.WriteTo(JsonArrayText); + + // [THEN] The retrieved value matches the expected value + Assert.AreEqual('[{"id":"ABC123"},{"id":"XYZ789"}]', JsonArrayText, 'The retrieved value does not match the expected value.'); + end; + + [Test] + procedure TestGetObjectAsText() + var + Json: Codeunit "Json"; + JsonObjectText: Text; + begin + // [GIVEN] A JSON object with a known value + Json.InitializeObject('{"id":"ABC123","name":"Test Name"}'); + + // [WHEN] Retrieve JSON object + JsonObjectText := Json.GetObjectAsText(); + + // [THEN] The retrieved value matches the expected value + Assert.AreEqual('{"id":"ABC123","name":"Test Name"}', JsonObjectText, 'The retrieved value does not match the expected value.'); + end; + + [Test] + procedure TestGetObject() + var + Json: Codeunit "Json"; + JsonObject: JsonObject; + JsonObjectText: Text; + begin + // [GIVEN] A JSON object with a known value + Json.InitializeObject('{"id":"ABC123","name":"Test Name"}'); + + // [WHEN] Retrieve JSON object + JsonObject := Json.GetObject(); + JsonObject.WriteTo(JsonObjectText); + + // [THEN] The retrieved value matches the expected value + Assert.AreEqual('{"id":"ABC123","name":"Test Name"}', JsonObjectText, 'The retrieved value does not match the expected value.'); + end; + + [Test] + procedure TestReplaceOrAddJPropertyInJObject() + var + Json: Codeunit "Json"; + JsonObjectText: Text; + NewJsonObjectText: Text; + begin + // [GIVEN] A JSON object with a known value + Json.InitializeObject('{"id":"ABC123","name":"Test Name"}'); + + // [WHEN] Replace a property in the JSON object + Json.ReplaceOrAddJPropertyInJObject('id', 'XYZ987'); + JsonObjectText := Json.GetObjectAsText(); + + // [THEN] The replaced value matches the expected value + Assert.AreEqual('{"id":"XYZ987","name":"Test Name"}', JsonObjectText, 'The replaced value does not match the expected value.'); + + // [WHEN] Add a new property to the JSON object + Json.ReplaceOrAddJPropertyInJObject('newProperty', 'New Property Value'); + NewJsonObjectText := Json.GetObjectAsText(); + + // [THEN] The added value matches the expected value + Assert.AreEqual('{"id":"XYZ987","name":"Test Name","newProperty":"New Property Value"}', NewJsonObjectText, 'The added value does not match the expected value.'); + end; + + [Test] + procedure TestReplaceJObjectInCollection() + var + Json: Codeunit "Json"; + JsonArrayText: Text; + NewJsonArrayText: Text; + begin + // [GIVEN] A JSON array with a known value + Json.InitializeCollection('[{"id":"ABC123"},{"id":"XYZ789"}]'); + + // [WHEN] Replace JSON object in the JSON array + Json.ReplaceJObjectInCollection(0, '{"id":"DYK484"}'); + JsonArrayText := Json.GetCollectionAsText(); + + // [THEN] The replaced value matches the expected value + Assert.AreEqual('[{"id":"DYK484"},{"id":"XYZ789"}]', JsonArrayText, 'The replaced value does not match the expected value.'); + + // [WHEN] Replace JSON object in the JSON array + Json.ReplaceJObjectInCollection(1, '{"id":"ZXY987"}'); + NewJsonArrayText := Json.GetCollectionAsText(); + + // [THEN] The replaced value matches the expected value + Assert.AreEqual('[{"id":"DYK484"},{"id":"ZXY987"}]', NewJsonArrayText, 'The replaced value does not match the expected value.'); + end; + + [Test] + procedure TestAddJObjectToCollection() + var + Json: Codeunit "Json"; + JsonArrayText: Text; + begin + // [GIVEN] A JSON array with a known value + Json.InitializeCollection('[{"id":"ABC123"},{"id":"XYZ789"}]'); + + // [WHEN] Add JSON object to the JSON array + Json.AddJObjectToCollection('{"id":"DYK484"}'); + JsonArrayText := Json.GetCollectionAsText(); + + // [THEN] The added value matches the expected value + Assert.AreEqual('[{"id":"ABC123"},{"id":"XYZ789"},{"id":"DYK484"}]', JsonArrayText, 'The added value does not match the expected value.'); + end; + + [Test] + procedure TestRemoveJObjectFromCollection() + var + Json: Codeunit "Json"; + JsonArrayText: Text; + begin + // [GIVEN] A JSON array with a known value + Json.InitializeCollection('[{"id":"ABC123"},{"id":"XYZ789"},{"id":"DYK484"}]'); + + // [WHEN] Remove JSON object from the JSON array + Json.RemoveJObjectFromCollection(1); + JsonArrayText := Json.GetCollectionAsText(); + + // [THEN] The removed value matches the expected value + Assert.AreEqual('[{"id":"ABC123"},{"id":"DYK484"}]', JsonArrayText, 'The removed value does not match the expected value.'); + end; +}