Two strategies:
1. Type-safe parsing with classes mirroring the JSON structure.
json { "id": "123", "name": "Acme", "address": { "city": "SF", "country": "US" }, "tags": ["enterprise", "priority"] }
`apex public class CompanyDto { public String id; public String name; public AddressDto address; public List<String> tags; } public class AddressDto { public String city; public String country; }
CompanyDto company = (CompanyDto) JSON.deserialize(jsonString, CompanyDto.class); System.debug(company.address.city); `
Pros: type-safe, IDE autocomplete, refactor-friendly. Cons: requires defining classes for every shape; field names must match exactly (case-sensitive); JSON.deserialize ignores extra fields silently (use JSON.deserializeStrict to fail on extras).
2. Untyped parsing with Map<String, Object>.
apex Object parsed = JSON.deserializeUntyped(jsonString); Map<String, Object> root = (Map<String, Object>) parsed; String name = (String) root.get('name'); Map<String, Object> address = (Map<String, Object>) root.get('address'); String city = (String) address.get('city'); List<Object> tags = (List<Object>) root.get('tags');
Pros: works for any JSON shape, even varying ones. Cons: lots of casting, runtime errors if structure isn't what you expect.
Patterns for tricky cases:
Field name mismatches (JSON is_active, Apex isActive):
apex String json = jsonStr.replace('"is_active"', '"isActive"'); // crude but works CompanyDto dto = (CompanyDto) JSON.deserialize(json, CompanyDto.class);
Or do a manual untyped parse and assign.
Reserved keywords (JSON class, Apex won't allow as field name):
Use apex_class in your DTO and rename in the JSON before parsing.
Optional / null fields: Apex deserialization handles missing fields by setting to null. JSON.deserializeStrict errors on unknown fields but tolerates missing.
Polymorphic structures (one of multiple shapes): parse untyped, then dispatch.
Performance:
- Type-safe is slightly faster.
- Both have heap costs proportional to JSON size — large payloads (>1 MB) need streaming, which Apex doesn't natively support. For really large JSON, parse in chunks via async.
Best practice: type-safe DTO classes for stable APIs; untyped Map for dynamic / unknown payloads.
