Study guide
16.2 JSON Processing: JsonObject, JsonArray, JsonValue & JsonToken
Microsoft Dynamics 365 Business Central Developer MB-820
Track your progress as you read
Finished sections get a check mark.
1 Chapter 1: Business Central Architecture & Platform Foundation
1.1 Business Central Architecture & Extensions Model 1.2 Customization Lifecycle: Cloud (SaaS) vs. On-Premises 1.3 Base Application, System Application & AppSource
2 Chapter 2: Development Environment & Configuration
2.1: Development Environment Setup: VS Code, AL: Go! Projects & Sandboxes 2.2: AL Configuration Files: app.json & launch.json Parameters 2.3: Multi-Root Workspaces & Project Dependencies
3 Chapter 3: Extension Lifecycle: Debugging, Deployment, Multilanguage & Maintenance
3.1 Debugging & Snapshot Debugging in Visual Studio Code 3.2 Extension Deployment & Schema Synchronization Modes 3.3 Multilanguage Development with XLIFF & Translation Files 3.4 Extension Maintenance, Deprecation & Breaking Changes
4 Chapter 4: Tables, Table Extensions & Data Modeling
4.1: Building Tables, Fields, Keys & Data Types 4.2: Table Extensions & Table Modifications 4.3: FlowFields, FlowFilters & Table Relations 4.4: Enums, Enum Extensions & Extensible Types
5 Chapter 5: Pages, Page Extensions & Role Centers
5.1: Page Types, Anatomy & Control Structure 5.2: Building Card, List & Document Pages 5.3: Page Extensions & Page Customizations 5.4: Role Centers, Headlines & Cue Data Modeling
6 Chapter 6: Reports & Document Generation
6.1 Report Architecture, DataItems & Dataset Design 6.2 Report Layouts: RDLC, Word, Excel & Multilanguage 6.3 Document Reports & Request Page Programming 6.4 Processing-Only Reports & Report Substitutions
7 Chapter 7: XMLports & Data Exchange
7.1 XMLport Architecture, Formats & Properties 7.2 XMLport Node Hierarchy, Triggers & Stream Manipulation
8 Chapter 8: Codeunits, Events, Triggers & Interfaces
8.1 Codeunit Types, Execution & SingleInstance Codeunits 8.2 Events, Publishers, Subscribers & Event Isolation 8.3 Interfaces, Polymorphism & Dynamic Codeunit Invocation 8.4 Installation & Upgrade Codeunits, Data Transfer & Archive
9 Chapter 9: Entitlements, Permissions & Queries
9.1 Permission Sets & Permission Set Extensions 9.2 Inherent Permissions, Inherent Entitlements & Security Scoping 9.3 Query Objects: Joins, Aggregation & Query as Report Data Source
10 Chapter 10: UI Customization, User Assistance & Onboarding
10.1 Profile Objects & Custom Page Views in AL 10.2 User Assistance: ToolTips, Teaching Tips & In-App Tours 10.3 Assisted Setup, Guided Experience & Onboarding Checklists
11 Chapter 11: Business Central Design Patterns & Data Process Model
11.1 Functional Table Types: Master Data, Supplemental & Setup Patterns 11.2 Document Standards: Header, Line, History & Journal Architectures 11.3 Data Process Model & Posting Routine Architecture
12 Chapter 12: AL Language Fundamentals, Data Types & Variables
12.1 AL Syntax, Variable Scope, Primitive & Complex Data Types 12.2 Expressions, Control Flow Statements & Built-in Functions 12.3 Access Modifiers (Public, Internal, Local) & Code Scoping
13 Chapter 13: Data Manipulation, Streams & Error Handling
13.1 Record Operations: Get, FindSet, ModifyAll & Filter Functions 13.2 Streams, File Management & TempBlob 13.3 Error Handling: ErrorInfo, TryFunctions & Collectible Errors
14 Chapter 14: Testing, Test Toolkit & Automation
14.1 Testing Architecture & the Test Toolkit 14.2 Authoring Test Codeunits & UI Handler Functions
15 Chapter 15: Telemetry, Performance Optimization & Analyzers
15.1 Telemetry Configuration & Azure Application Insights 15.2 Custom Telemetry Signals with Session.LogMessage & KQL 15.3 Performance Profiler, SetLoadFields & Query Optimization 15.4 AL Analyzers: CodeCop, AppSourceCop, UICop & PTECop
16 Chapter 16: REST Web Services & JSON in AL
16.1 HttpClient, HttpRequestMessage & REST API Consumption 16.2 JSON Processing: JsonObject, JsonArray, JsonValue & JsonToken
17 Chapter 17: API Pages, OData, Read Scale-Out & OAuth
17.1 API Pages (PageType=API), OData Web Services & REST Endpoints 17.2 Bound and Unbound OData Actions with [ServiceEnabled] 17.3 Read Scale-Out Architecture & DataAccessIntent Configuration 17.4 Microsoft Entra ID App Registrations & OAuth 2.0 Integration
16.2 JSON Processing: JsonObject, JsonArray, JsonValue & JsonToken
Key Takeaways Explain This Topic AI
The AL JSON object hierarchy is structured around JsonToken as the universal base class, which is polymorphically specialized into JsonObject (key-value dictionary), JsonArray (ordered collection), and JsonValue (primitive scalar or null literal).
JsonObject.ReadFrom(Text/InStream) and JsonToken.ReadFrom(Text/InStream) parse JSON payloads and return a Boolean indicating success, preventing unhandled runtime exceptions on malformed or empty payloads.
Properties are retrieved using JsonObject.Get('key', JsonToken); the resulting token must be inspected with .IsObject(), .IsArray(), or .IsValue() and downcast using .AsObject(), .AsArray(), or .AsValue().
Primitive values are extracted from JsonValue using .AsText(), .AsInteger(), .AsDecimal(), .AsBoolean(), .AsDate(), or .AsDateTime(); calling these methods on a null JsonValue immediately triggers a fatal runtime exception.
Defensive JSON processing requires verifying property presence with JsonObject.Contains('key') and checking JsonToken.IsNull() or JsonValue.IsNull() before attempting type downcasting and scalar extraction.
Last updated: August 2026
16.2 JSON Processing: JsonObject, JsonArray, JsonValue & JsonToken
JavaScript Object Notation (JSON) is the universal data interchange format for modern RESTful web services, cloud APIs, microservice architectures, and cloud ERP integrations. Dynamics 365 Business Central provides a rich, strongly typed JSON object model built directly into the AL runtime. Unlike legacy platforms that relied on external COM libraries, .NET interoperability ( Newtonsoft.Json ), or XML DOM parsers, AL includes four native, high-performance reference data types: JsonToken , JsonObject , JsonArray , and JsonValue .
For the MB-820 (Microsoft Dynamics 365 Business Central Developer Associate) examination, developers must thoroughly master the polymorphic relationship between these types, parse complex nested JSON streams, extract and convert scalar values safely, construct and serialize outbound payloads, and prevent fatal runtime crashes caused by missing keys or JSON null literals.
1. AL JSON Type Hierarchy & Polymorphism
The AL JSON architecture is structured around JsonToken , which serves as the universal abstract base class for all JSON entities. Every JSON node—whether an entire JSON document, a nested dictionary, an array of records, a single string value, a numeric scalar, a boolean flag, or an explicit null literal—can be stored in and represented by a JsonToken . [ JsonToken ]
- (Universal Base Class Node)
- │
- ┌─────────────────────────────┼─────────────────────────────┐
- ▼ ▼ ▼
- [ JsonObject ] [ JsonArray ] [ JsonValue ]
- (Key-Value Dictionary) (Ordered Collection) (Scalar / Primitive / Null)
- Contains(Key) - Count() - AsText()
- Get(Key, JsonToken) - Get(Index, JsonToken) - AsInteger()
- Add(Key, Value) - Add(JsonToken) - AsDecimal()
- Remove(Key) - RemoveAt(Index) - AsBoolean()
- Replace(Key, Value) - IndexOf(JsonToken) - AsDate() / AsDateTime()
- Keys() - Clear() - IsNull()
- The Four JSON Types Compared
AL Type JSON Representation Primary Purpose Key Methods
JsonToken Any JSON element ( {} , [] , "text" , 123 , true , null ) The polymorphic container used when reading, navigating, or traversing unknown or dynamic JSON structures. ReadFrom(Text | InStream) ; WriteTo(Text | OutStream) ; IsObject() , IsArray() , IsValue() ; IsNull() , IsUndefined() ; AsObject() , AsArray() , AsValue()
JsonObject { "key": value, ... } Represents an unordered collection of string key-value pairs (JSON Object). ReadFrom() , WriteTo() ; Get(Key, JsonToken) ; Add(Key, Value) ; Contains(Key) ; Remove(Key) ; Replace(Key, Value) ; Keys()
JsonArray [ value1, value2, ... ] Represents an ordered zero-indexed list of JsonToken items (JSON Array). ReadFrom() , WriteTo() ; Get(Index, JsonToken) ; Add(JsonToken) ; Count() ; RemoveAt(Index) ; IndexOf(JsonToken)
JsonValue "string" , 100 , 45.50 , true , false , null Encapsulates a single primitive scalar value, date, or null literal. AsText() , AsInteger() , AsDecimal() ; AsBoolean() , AsDate() , AsDateTime() ; SetValue(Value) ; IsNull()
2. Parsing JSON Payloads and Navigating Structures
When processing JSON returned from an external REST API, developers read the payload into memory using ReadFrom() and traverse nested tokens using typed accessor methods.
Safe Ingestion with ReadFrom()
Both JsonObject and JsonToken provide ReadFrom() methods accepting either a Text variable or an InStream :
- JsonObject.ReadFrom(JsonText: Text): Boolean
- JsonObject.ReadFrom(InStream: InStream): Boolean
- JsonToken.ReadFrom(JsonText: Text): Boolean
- JsonToken.ReadFrom(InStream: InStream): Boolean
ReadFrom() returns a Boolean indicating whether the text conforms to valid JSON syntax. If the input string is empty or contains malformed syntax, ReadFrom() returns false without raising an unhandled runtime exception, allowing AL developers to implement clean diagnostic logging and graceful error recovery.
Navigating and Downcasting JsonToken
In AL, properties cannot be accessed using dot-notation (e.g., Json.Customer.Name is invalid syntax). Instead, developers navigate properties using JsonObject.Get() and downcast the resulting JsonToken :
Inspect Token Type:
Token.IsObject(): Boolean — Returns true if the node is a {} dictionary.
Token.IsArray(): Boolean — Returns true if the node is a [] list.
Token.IsValue(): Boolean — Returns true if the node is a scalar primitive or null .
Downcast Token:
Token.AsObject(): JsonObject — Casts token to JsonObject . Throws runtime error if not Token.IsObject() .
Token.AsArray(): JsonArray — Casts token to JsonArray . Throws runtime error if not Token.IsArray() .
Token.AsValue(): JsonValue — Casts token to JsonValue . Throws runtime error if not Token.IsValue() .
Extract Primitive Data from JsonValue :
- JsonValue.AsText(): Text
- JsonValue.AsInteger(): Integer
- JsonValue.AsDecimal(): Decimal
- JsonValue.AsBoolean(): Boolean
- JsonValue.AsDate(): Date
- JsonValue.AsDateTime(): DateTime
- Comprehensive AL Parsing Example: Ingesting Complex Nested Documents
- Consider the following external JSON response from an e-commerce platform: {
- "orderId": "ORD-99201",
- "customer": {
- "number": "C00010",
- "name": "Contoso Retail"
- },
- "posted": true,
- "totalAmount": 1450.75,
- "lines": [
- { "lineNo": 10000, "itemNo": "1001", "quantity": 2, "unitPrice": 500.00 },
- { "lineNo": 20000, "itemNo": "1002", "quantity": 1, "unitPrice": 450.75 }
- ]
- }
- The following AL procedure parses this complex document safely: codeunit 50125 "JSON Ingestion Handler"
- {
- procedure ParseOrderResponse(ResponseJson: Text)
- var
- RootObj: JsonObject;
- CustomerObj: JsonObject;
- LinesArray: JsonArray;
- LineToken: JsonToken;
- LineObj: JsonObject;
- Token: JsonToken;
- OrderId: Text;
- CustomerNo: Code[20];
- TotalAmount: Decimal;
- IsPosted: Boolean;
- ItemNo: Code[20];
- Quantity: Decimal;
- begin
- // 1. Safe parsing of root JSON object
- if not RootObj.ReadFrom(ResponseJson) then
- Error('Invalid JSON format received from server.');
- // 2. Extract scalar string property
- if RootObj.Get('orderId', Token) then
- OrderId := Token.AsValue().AsText();
- // 3. Extract boolean and decimal scalar properties
- if RootObj.Get('posted', Token) then
- IsPosted := Token.AsValue().AsBoolean();
- if RootObj.Get('totalAmount', Token) then
- TotalAmount := Token.AsValue().AsDecimal();
- // 4. Navigate nested JsonObject ('customer')
- if RootObj.Get('customer', Token) then begin
- CustomerObj := Token.AsObject();
- if CustomerObj.Get('number', Token) then
- CustomerNo := CopyStr(Token.AsValue().AsText(), 1, MaxStrLen(CustomerNo));
- end;
- // 5. Navigate and iterate through JsonArray ('lines')
- if RootObj.Get('lines', Token) then begin
- LinesArray := Token.AsArray();
- // Iterate collection using foreach over JsonToken
- foreach LineToken in LinesArray do begin
- LineObj := LineToken.AsObject();
- if LineObj.Get('itemNo', Token) then
- ItemNo := CopyStr(Token.AsValue().AsText(), 1, MaxStrLen(ItemNo));
- if LineObj.Get('quantity', Token) then
- Quantity := Token.AsValue().AsDecimal();
- InsertSalesLine(OrderId, ItemNo, Quantity);
- end;
- end;
- end;
- local procedure InsertSalesLine(OrderId: Text; ItemNo: Code[20]; Qty: Decimal)
- begin
- // Database insert routine
- end;
- }
Loading diagram... AL JsonToken Polymorphic Navigation and Value Downcasting Pipeline
3. Constructing and Serializing JSON Payloads
When sending data from Business Central to external systems, AL developers construct JsonObject and JsonArray graphs programmatically and serialize them to string or stream variables.
Constructing Payloads with JsonObject.Add()
JsonObject.Add() is overloaded to accept a string key name alongside various value types:
- JsonObject.Add(Key: Text, Value: Text)
- JsonObject.Add(Key: Text, Value: Integer)
- JsonObject.Add(Key: Text, Value: Decimal)
- JsonObject.Add(Key: Text, Value: Boolean)
- JsonObject.Add(Key: Text, Value: JsonToken) (attaches nested JsonObject or JsonArray )
Similarly, JsonArray.Add() accepts any JsonToken (or scalar value) and appends it to the end of the array.
Modifying Existing Objects with Replace and Remove
JsonObject.Replace(Key: Text, Value: Text | Integer | Decimal | Boolean | JsonToken): Boolean : Replaces the value of an existing key. If the key does not exist, it throws a runtime error or returns false .
JsonObject.Remove(Key: Text): Boolean : Removes the specified key and its associated value from the JsonObject . Returns true if the key was found and removed.
Serializing JSON with WriteTo()
To transform an in-memory JSON structure into an outbound payload:
JsonObject.WriteTo(JsonText: Text): Boolean : Serializes the JSON graph into an AL Text variable.
JsonObject.WriteTo(OutStream: OutStream): Boolean : Serializes directly into an OutStream . This is highly recommended when generating large payloads to avoid holding massive strings in memory.
Comprehensive Payload Construction Example procedure GenerateBatchCustomerSyncPayload(var Customer: Record Customer): Text
- var
- RootObj: JsonObject;
- MetadataObj: JsonObject;
- CustomersArray: JsonArray;
- CustObj: JsonObject;
- SerializedPayload: Text;
- begin
- // 1. Build metadata header object
- MetadataObj.Add('sourceSystem', 'Dynamics 365 Business Central');
- MetadataObj.Add('exportTimestamp', CurrentDateTime());
- MetadataObj.Add('companyName', CompanyName());
- RootObj.Add('metadata', MetadataObj);
- // 2. Iterate records and construct customer objects in array
- if Customer.FindSet() then
- repeat
- Clear(CustObj);
- CustObj.Add('customerNumber', Customer."No.");
- CustObj.Add('displayName', Customer.Name);
- CustObj.Add('balanceLCY', Customer."Balance (LCY)");
- CustObj.Add('creditLimitLCY', Customer."Credit Limit (LCY)");
- CustObj.Add('blocked', Format(Customer.Blocked));
- CustomersArray.Add(CustObj);
- until Customer.Next() = 0;
- // 3. Attach array to root document
- RootObj.Add('customerCount', CustomersArray.Count());
- RootObj.Add('customers', CustomersArray);
- // 4. Serialize to string
- RootObj.WriteTo(SerializedPayload);
- exit(SerializedPayload);
- end;
- 4. Robust Null and Missing Property Handling
In real-world REST integrations, external APIs frequently omit optional properties or return JSON null literals (e.g., "taxExemptNumber": null ). Failing to anticipate these edge cases causes fatal AL runtime errors.
Missing Key vs. JSON Null
Missing Key: The key does not exist in the JsonObject at all ( { "name": "Contoso" } ). Calling RootObj.Get('taxExemptNumber', Token) returns false without modifying Token .
JSON Null Literal: The key exists, but its value is explicitly null ( { "taxExemptNumber": null } ). Calling RootObj.Get('taxExemptNumber', Token) returns true , and Token is initialized with a JsonValue representing null .
Fatal Error Alert: If you call .AsText() , .AsDecimal() , or .AsInteger() on a JsonValue that is null, the AL runtime crashes immediately with an unhandled exception ( "Cannot convert null value to..." ). You must verify that Token.IsNull() or JsonValue.IsNull() is false before invoking conversion methods.
Safe Property Extractor Pattern in AL
To write clean, enterprise-grade integration code, implement reusable helper functions that verify key existence and null status before attempting value conversion: codeunit 50126 "JSON Safe Helpers"
- {
- procedure GetJsonValueAsText(var JObject: JsonObject; PropertyKey: Text; DefaultValue: Text): Text
- var
- JToken: JsonToken;
- begin
- if not JObject.Contains(PropertyKey) then
- exit(DefaultValue);
- if not JObject.Get(PropertyKey, JToken) then
- exit(DefaultValue);
- if JToken.IsNull() or JToken.IsUndefined() then
- exit(DefaultValue);
- if not JToken.IsValue() then
- exit(DefaultValue);
- if JToken.AsValue().IsNull() then
- exit(DefaultValue);
- exit(JToken.AsValue().AsText());
- end;
- procedure GetJsonValueAsDecimal(var JObject: JsonObject; PropertyKey: Text; DefaultValue: Decimal): Decimal
- var
- JToken: JsonToken;
- begin
- if not JObject.Contains(PropertyKey) then
- exit(DefaultValue);
- if not JObject.Get(PropertyKey, JToken) then
- exit(DefaultValue);
- if JToken.IsNull() or JToken.IsUndefined() or (not JToken.IsValue()) then
- exit(DefaultValue);
- if JToken.AsValue().IsNull() then
- exit(DefaultValue);
- exit(JToken.AsValue().AsDecimal());
- end;
- procedure GetJsonValueAsDate(var JObject: JsonObject; PropertyKey: Text; DefaultValue: Date): Date
- var
- JToken: JsonToken;
- begin
- if not JObject.Contains(PropertyKey) then
- exit(DefaultValue);
- if not JObject.Get(PropertyKey, JToken) then
- exit(DefaultValue);
- if JToken.IsNull() or JToken.IsUndefined() or (not JToken.IsValue()) then
- exit(DefaultValue);
- if JToken.AsValue().IsNull() then
- exit(DefaultValue);
- exit(JToken.AsValue().AsDate());
- end;
- }