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;
+}