Integrating Salesforce with external APIs often leads to a common frustration: the JSON payload contains keys that are reserved words in Apex. If an API returns a property named currency, object, private, or class, you cannot simply create a matching variable in your Apex class. Attempting to do so will result in a compilation error because these identifiers are protected by the language.

In this guide, you will learn several strategies to overcome this limitation, ranging from simple string manipulation to advanced DataWeave transformations.

The Problem: Identifier Name is Reserved

Imagine you are consuming a service that returns the following JSON:

{
  "currency": "USD",
  "object": "Account",
  "private": true
}

You might attempt to define a Data Transfer Object (DTO) like this:

public class JSONResult {
    public String currency; // Compile Error: Identifier name is reserved
    public String object;   // Compile Error: Identifier name is reserved
}

Since Apex won't let you save this class, you need a way to map these "illegal" keys to valid Apex variable names like currency_x or obj during the deserialization process.

Strategy 1: The String Replacement Method

The fastest way to handle a reserved word is to modify the JSON string before it reaches the deserializer. You can replace the problematic key with a safe alternative.

String jsonString = '{"currency" : "ABC"}';

// Replace the key with a safe version
jsonString = jsonString.replace('"currency":', '"currency_x":');

public class JSONResult {
    public String currency_x;
}

JSONResult res = (JSONResult) JSON.deserialize(jsonString, JSONResult.class);
System.debug(res.currency_x); // Outputs: ABC

Pros: Extremely simple to implement for one or two fields. Cons: This can be risky. If the value of a field (rather than the key) contains the same string, you might accidentally corrupt your data. To mitigate this, ensure you include the quotes and colon in your search string (e.g., "currency":).

Strategy 2: Using Untyped Deserialization

If you want to avoid creating a DTO class altogether, or if the JSON structure is dynamic, you can use JSON.deserializeUntyped. This method returns a Map<String, Object>, and Apex maps allow any string as a key—even reserved words.

String jsonString = '{"currency": "USD", "object": "Lead"}';
Map<String, Object> root = (Map<String, Object>)JSON.deserializeUntyped(jsonString);

String currencyValue = (String)root.get('currency');
String objectValue = (String)root.get('object');

System.debug('Currency: ' + currencyValue);

Pros: No class definition required; handles any reserved word naturally. Cons: You lose type safety and the convenience of dot notation. You must manually cast values to their expected types.

Strategy 3: Modern Transformation with DataWeave in Apex

As of the Winter '24 release, DataWeave in Apex is Generally Available (GA). This is the most robust and "Salesforce-native" way to handle complex JSON transformations. You can write a small DataWeave script to rename keys before they hit your Apex logic.

First, create a DataWeave script (e.g., renameKeywords.dwl) in your project's /dw folder:

%dw 2.0 
input incomingJson application/json
output application/json
fun renameKey(key: Key) = key match {
    case "currency" -> "currency_x"
    case "object" -> "obj"
    case "private" -> "isPrivate"
    else -> (key)
}
--- 
incomingJson mapObject (value, key) -> {
    (renameKey(key)) : value
}

Then, call this script from your Apex code:

String jsonString = '{"currency" : "ABC"}';

// Execute the DataWeave script
DataWeave.Script script = new DataWeaveScriptResource.renameKeywords();
DataWeave.Result result = script.execute(new Map<String, Object>{ 'incomingJson' => jsonString });

String transformedJson = result.getValueAsString();

// Now deserialize into your safe class
JSONResult res = (JSONResult) JSON.deserialize(transformedJson, JSONResult.class);

Pros: Scalable, handles complex nested logic, and keeps transformation logic separate from business logic. Cons: Requires setup of DataWeave resources in your metadata.

Strategy 4: The Recursive Suffixing Utility

If you have a deeply nested JSON and don't want to use DataWeave, you can write a utility class to recursively traverse the JSON (via untyped maps) and append a suffix like _x to every key. This allows you to define your DTOs with _x suffixes for every field.

public class ReservedWordSerializer {
    public static String safeSerialize(String jsonString) {
        Object untyped = JSON.deserializeUntyped(jsonString);
        // Recursively traverse and append '_x' to keys
        // (See implementation details for map/list traversal)
        return JSON.serialize(processObject(untyped));
    }

    private static Object processObject(Object obj) {
        if (obj instanceof Map<String, Object>) {
            Map<String, Object> newMap = new Map<String, Object>();
            Map<String, Object> oldMap = (Map<String, Object>)obj;
            for (String key : oldMap.keySet()) {
                newMap.put(key + '_x', processObject(oldMap.get(key)));
            }
            return newMap;
        }
        return obj;
    }
}

Wrapping Up

When dealing with reserved words in Apex JSON deserialization, your choice of strategy depends on the complexity of your integration:

  • For simple, single-key issues: Use String.replace().
  • For quick, non-typed access: Use JSON.deserializeUntyped().
  • For enterprise-grade, scalable integrations: Use DataWeave in Apex.

By renaming these keys during the pre-processing phase, you can maintain clean, type-safe Apex code while still communicating effectively with external services that use different naming conventions.