Parse JSON with Reserved Words

JSON Logo
One of the great things about Salesforce when dealing with external webservices is being able to easily parse JSON into Apex classes.  I’ve covered this in several previous posts.   However a common problem is that the system you are integrating with is they may be using a variable name that is reserved.  With the following data, we can see that there is a variable named “case” if we ant to parse this data into a Apex class we won’t be able to because case is a reserved name.

{
    "data": [
        {
            "case": "123456",
            "subject": "Test case"
        }, {
            "case": "789012",
            "subject": "Another case"
        }
    ]
}

If we tried to make a wrapper class for this called CaseData with the format below, we’d get an error stating “Identifier name is reserved: case”

public class CaseData {
    public String case;
    public String subject;
}

So, one way to work around this is to not use the reserved name for the variable.  So if we make a new CaseData with following format we can save the class.

public class CaseData {
    public String case_x;
    public String subject;
}

And now if we execute the following Apex, we can pull the data into an array of CaseData objects

public class GistParser {
    public class CaseData {
        public String case_x;
        public String subject;
    }

    public class DataWrapper {
        public List<CaseData> data;
    }

    public static void readEndpoint() {
        String ENDPOINT = 'https://gist.githubusercontent.com/pcon/a12c84e2ef54370c25c26fc19f331971/raw/6a881590a5ca1467283ac17bea223a8916ce9528/gistfile1.txt';

        HttpRequest req = new HttpRequest();
        req.setMethod('GET');
        req.setEndpoint(ENDPOINT);
        Http h = new Http();
        HttpResponse res = h.send(req);
        System.debug(JSON.deserialize(res.getBody(), DataWrapper.class));
    }
}

Now, while this will save it will not store our case number into the case_x variable because that is not the name of the field in the JSON data.  To work around this, we can do a search and replace on our incoming body.  The simplest way is to call

System.debug(JSON.deserialize(res.getBody().replace('"case":', '"case_x":'), DataWrapper.class));

This search and replace works but is dangerous and honestly doesn’t scale too well.  So, let’s take a look at a way to make it scale better

public static String mogrifyJSON(String data) {
    // Regex to match the start of the line and the key
    // surrounded by quotes and ending with a colon
    String regexFormat = '(?m)^\\s*"{0}"\\s*:';
    
    // Replacement format of the new key surrounded by
    // quotes and ending with a colon
    String replacementFormat = '"{0}" :';
    
    // A map of existing key to replacement key
    Map<String, String> replacements = new Map<String, String> {
        'case' => 'case_x'
    };
    
    // Since our JSON can come in formatted however the
    // endpoint wants, we need to format it to a standard
    // we know and can handle
    String formattedJSON = JSON.serializePretty(JSON.deserializeUntyped(data));
    
    // Iterate over all the keys we want to replace
    for (String key : replacements.keySet()) {
        // Generate our regex based on the key
        String regex = String.format(
            regexFormat,
            new List<String> {key}
        );
        
        // Generate our replacement
        String replacement = String.format(
            replacementFormat,
            new List<String> {replacements.get(key)}
        );
        
        // Find all and replace
        formattedJSON = formattedJSON.replaceAll(regex, replacement);
    }
    
    return formattedJSON;
}

This code iterates over a map of old keys to new keys and does a find and replace on all of them.  This method is “safer” because we first reformat the JSON into a known good format so that we only replace the initial key.  This prevents us from accidentally replacing a match for the key in the middle of the data.  We can then call it by saying

System.debug(JSON.deserialize(mogrifyJSON(res.getBody()), DataWrapper.class));
This entry was posted in Development, Salesforce and tagged , , . Bookmark the permalink.
  • Matthew Mitchener

    Great post. I feel like you could take this one step further and create your own JSON deserializer that handles the mogrification (great word btw!)

    It would end up looking a little cleaner: JSONUtils.deserialize(res.getBody(), DataWrapper.class);

    Seems like that work in theory, but not sure if you could pass DataWrapper.class as a parameter for a apex method.

  • You could, but you end up with the problem of having to maintain, test and debug your own JSON deserializer. With that being said, if you’re only pulling out one part you could always use something like apex-lodash [1] and just pluck it from the bowels of the JSON tree.

    If I were to use this in my main code base, I would most definitely put it into a utility class that behind the covers uses the code in mogrifyJSON and does all the substitutions.

    I wish I could take credit for the mogrify word, but it’s the name of the tool that ImageMagick uses [2] to modify images in place.

    [1] https://github.com/apex-lodash/lo
    [2] http://www.imagemagick.org/script/mogrify.php

  • Matthew Mitchener

    Yea, that’s a good point. I’ll have to play around with your library, it looks neat. Big fan of lodash for javascript.

  • Carlos Naranjo

    Thank you Patrick, very helpful post you are sharing here. Thanks!