Modern enterprise software requires seamless integration between diverse cloud services. In the Salesforce ecosystem, REST API integrations frequently communicate using JSON (JavaScript Object Notation). To process this data, Salesforce developers must understand how to serialize (convert objects to JSON strings) and deserialize (convert JSON strings back to Apex typed objects) effectively.

Native Apex JSON Support

Apex provides a built-in system class called System.JSON, which includes static methods designed to parse and construct JSON payloads. The two primary approaches to parsing JSON in Apex are **typed parsing** and **untyped parsing**.

Type-Safe Deserialization (Typed Parsing)

Typed deserialization is the preferred approach for structured payloads where the schema is consistent. It maps JSON fields directly to an Apex class structure, providing strong typing, compilation safety, and autocomplete support in IDEs.

Using the JSON.deserialize() method, you instruct the Apex compiler to map properties directly to your custom class type:

// Define your custom class structure
public class UserWrapper {
    public String name;
    public Integer age;
    public Boolean isActive;
}

// Deserialize the JSON string
String jsonInput = '{"name": "John Doe", "age": 35, "isActive": true}';
UserWrapper user = (UserWrapper) JSON.deserialize(jsonInput, UserWrapper.class);

System.debug('User Name: ' + user.name); // Output: John Doe

This is where utility tools like **json2apex** excel. Rather than manually writing wrapper structures for nested, complex JSON arrays and variables, developers can paste their payload and receive a typed wrapper class instantly.

Untyped Parsing (Map-Based)

When APIs return dynamic JSON payloads with variable keys or dynamic property names, static wrapper classes might fail or feel overly rigid. In these cases, untyped parsing is useful. Using JSON.deserializeUntyped(), Apex constructs a generic Map of keys to generic Objects:

String dynamicJson = '{"status": "success", "data": {"id": 102}}';
Map<String, Object> result = (Map<String, Object>) JSON.deserializeUntyped(dynamicJson);

String status = (String) result.get('status');
Map<String, Object> data = (Map<String, Object>) result.get('data');
Integer id = (Integer) data.get('id');

System.debug('Data ID: ' + id); // Output: 102

While dynamic, untyped parsing requires explicit typecasting and is more prone to runtime errors since compile-time validation is not possible.

Serializing Apex Objects to JSON

To call external REST web services, developers must construct JSON payload strings. The JSON.serialize() method converts any Apex object or collection into its corresponding JSON representation:

UserWrapper newUser = new UserWrapper();
newUser.name = 'Alice';
newUser.age = 30;
newUser.isActive = false;

String payload = JSON.serialize(newUser);
// Result: {"name":"Alice","age":30,"isActive":false}

Apex Governor Limits & JSON Performance

Salesforce runs in a multi-tenant environment, enforcing strict governor limits. The CPU time limit (10 seconds for synchronous transactions) is particularly relevant when handling heavy JSON documents. Large payloads of multiple megabytes can quickly consume CPU cycles during deserialization. Standardizing on typed wrapper classes is generally faster and uses less CPU time than recursive maps compiled during untyped parsing.

Conclusion

Understanding the distinction between typed and untyped JSON deserialization allows Salesforce developers to select the optimal integration architecture. For structured payloads, using generated Apex wrapper classes ensures code cleanliness, compiler support, and optimal transaction speed.