Every integration speaks JSON and AL has a decent set of types for it. Building a payload, parsing a response, handling missing and null properties without an error, nested objects and arrays, and the helper the base application now ships so you stop writing the same token-to-text code.
AL's JSON types (JsonObject, JsonArray, JsonToken, JsonValue) are reference types with a small, consistent API. Most JSON bugs in extensions come from two habits: assuming a property exists, and converting through Format. These are the patterns that avoid both.
procedure BuildShipment(Shipment: Record "CON Courier Shipment") Body: JsonObject
var
Addr: JsonObject;
Parcels: JsonArray;
Parcel: JsonObject;
begin
Body.Add('reference', Shipment."No.");
Body.Add('shipDate', Format(Shipment."Shipment Date", 0, 9)); // ISO 8601
Addr.Add('line1', Shipment."Ship-to Address");
Addr.Add('postcode', Shipment."Ship-to Post Code");
Body.Add('address', Addr);
Parcel.Add('weightKg', Shipment."Weight (kg)");
Parcels.Add(Parcel);
Body.Add('parcels', Parcels);
end;
Dates and times with Format(x, 0, 9) (the XML/ISO format), decimals as decimals (the type handles it; never Format a decimal into JSON, it will use the user's locale and produce a comma). Property names are technical strings, so they are locked labels in a real codebase; shown inline here for brevity.
procedure ReadTrackingNo(ResponseText: Text) TrackingNo: Text
var
Root: JsonObject;
Tok: JsonToken;
begin
if not Root.ReadFrom(ResponseText) then
Error(InvalidJsonErr);
if not Root.Get('trackingNumber', Tok) then
exit(''); // property missing: decide, don't crash
if Tok.AsValue().IsNull() then
exit('');
TrackingNo := Tok.AsValue().AsText();
end;
Always Get (returns false if missing) rather than indexing; check IsNull before AsText; use AsValue().AsDecimal(), AsBoolean(), AsDate() for typed reads. For nested paths, Root.SelectToken('address.postcode', Tok) accepts a JSONPath-style expression and is clearer than chained Gets.
if Root.Get('parcels', Tok) and Tok.IsArray() then
foreach Item in Tok.AsArray() do
ProcessParcel(Item.AsObject());
The base application ships Codeunit "JSON Management" (older) and, in the System Application, helpers for common conversions; most teams keep a small "CON Json Helper" with GetText(Obj, Path, Default), GetDecimal, GetDate that wrap the null and missing checks once. Write it once, test it once, and every integration reads the same way.
ReadFrom(InStream) and WriteTo(OutStream) avoid building a huge Text. For large arrays, process as you iterate rather than copying into temporary records first, unless you need to sort.
Tok.AsValue().AsText() to a number: compare typed.Replace if it might exist.