68

Is there any way to deserialize JSON into an object using JSON.deserialize if some of the property names in the JSON are reserved words in Apex? I want to do something like this:

string jsonString = '{"currency" : "ABC"}';
public class JSONResult
{
    public string currency;
}
JSONResult res = (JSONResult) JSON.deserialize(jsonString, JSONResult.class);
system.debug(res);

but of course I get an error that the identifier name is reserved.

4
  • In .Net you would prepend with '@'. Does Apex have something similar?
    – Mike Chale
    Oct 12, 2012 at 18:54
  • Not that I was able to find but I'm really hoping that someone will be able to correct me. Oct 12, 2012 at 19:02
  • Is it an option to deserialize as Map<String,String>? Jan 15, 2013 at 23:19
  • 2
    it wasn't in this case since I wanted to deserialize into fairly complex objects and going through the deserializeuntyped map would have been painful. I ended up using the string replace and it worked fairly well. Jan 15, 2013 at 23:35

5 Answers 5

67

There are 2 ways that you could solve this problem, neither of them is exactly what you're looking for, but I think it's the best apex offers.

  1. Perform a string replace on the json string, the implications of this is unintended replacement of valid text inside a string, but this could be mitigated if the form of the json is as you've supplied "currency": "ABC" you could string replace:

    jsonString.replace('"currency":', '"currency_x":');

  2. The other is a lot more painful, it would require you to parse the json yourself using the JSON Parser methods. This will be a lot more accurate, but if the definition of the json changes you will have to rewrite your solution

1
15

I've created a abstract class to allow for two-way serialization of JSON that has reserved keywords. There are definitely some limitations but it works for everything I've tried so far (google calendar & jira API).

/* Author: Charlie Jonas ([email protected])
 * Description: Allows reserved named serialization.
 * Usage:  See Readme @ https://github.com/ChuckJonas/APEX-JSONReservedNameSerializer
 */
public abstract class JSONReservedSerializer {
    private final Map<Type,Map<String,String>> typeMapKeys;

    public JSONReservedSerializer(Map<Type, Map<String, String>> typeMapKeys){
        this.typeMapKeys = typeMapKeys;
    }

    public String serialize(Object obj, System.Type type){
        return serialize(obj, false, type);
    }

    public String serialize(Object obj, Boolean suppressNulls, System.Type type){
        String retString = JSON.serialize(obj, suppressNulls);
        retString = transformStringForSerilization(retString, typeMapKeys.get(type));
        return retString;
    }

    public Object deserialize(String jsonString, System.Type type){
        jsonString = transformStringForDeserilization(jsonString, typeMapKeys.get(type));
        return JSON.deserialize(jsonString, type);
    }

    private static String transformStringForSerilization(String s, Map<String, String> mapKeys){
        return replaceAll(s, mapKeys);
    }

    private static String transformStringForDeserilization(String s, Map<String, String> mapKeys){
        Map<String,String> flippedMap = new Map<String,String>();
        for(String key : mapKeys.keySet()){
            flippedMap.put(mapKeys.get(key), key);
        }
        return replaceAll(s, flippedMap);
    }

    private static String replaceAll(String s, Map<String,String> toFromMap){
        for(String key : toFromMap.keySet()){
            s = s.replaceAll('"'+key+'"(\\ )*:', '"'+toFromMap.get(key)+'":');
        }
        return s;
    }
}

Implementation looks like this:

public class MySerializer extends JSONImprovedSerializer {

  private MySerializer() {
    //setup mappings
    super(new Map<Type,Map<String,String>>{
      MyOuterDTO.class => OUTER_DTO_MAPPINGS
    });
  }

  //define DTO's using mapped names
  static final Map<String, String> OUTER_DTO_MAPPINGS = new Map<String, String> {
      'obj' => 'object',
      'isPrivate' => 'private'
  };

  public class OuterDTO {
    public InnerDTO obj;
  }

  public class InnerDTO {
    public Boolean isPrivate;
    public String notReserved;
  }
}

Usage (round trip serialization):

String origString = '{"object":{"private":true,"notReserved":"abc"}}';

//deserialization
MySerializer json = new MySerializer();
MySerializer.OuterDTO dto = (MySerializer.OuterDTO) json.deserialize(
  origString,
  MySerializer.OuterDTO.class
);

//serialization
String newString = json.serialize(obj);
System.assertEquals(origString, newString);

UPDATE

I Created a to a repo with install instruction using the sfdx-cli

2
  • String.replace() is much shorter and much more performant Jun 2, 2023 at 14:58
  • @AlexKalatski sure is! Seems like a general statement you could apply to any class. And did you test how "much more performant" it actually is? Pretty sure when the essential operations are string.replace & json.seralize in both solutions, some class initialization probably is pretty insignificant.
    – NSjonas
    Jun 2, 2023 at 20:19
6

To add to the 2 ways in the accepted answer, here is a third way making use of Apex maps that allow any string as the key:

Map<String, Object> m = (Map<String, Object>)JSON.deserializeUntyped('{"QPA$MV2": "xyz"}');
String value = (String) m.get('QPA$MV2');
System.assertEquals('xyz', value);

This approach also works when you want to serialize to JSON.

6

Can suffix the JSON programmatically and deserialize it into a generated class:

public class DTO {
    String toString_x;
    String object_x;
    String class_x;
    String new_x;
}

For example:

String data = '{"class": ""}'; //bad words etc...
Object input = Json.deserializeUntyped(data);

String suffixed = new ReservedWordSerializer(obj).getAsString();
DTO dto = (DTO)Json.deserialize(suffixed, DTO.class);

//no more bad words
System.debug(dto.class_x);

using the below generator to perform the suffixing:

/**
 * Usage:
 * new ReservedWordSerializer(obj).getAsString();
 */
public class ReservedWordSerializer
{
    
    // true for pretty printing
    JsonGenerator g = Json.createGenerator(true);
    
    public ReservedWordSerializer(Object obj)
    {
        if (obj == null)
        {
            g.writeNull();
        }
        else if (obj instanceof Map<String,Object>)
        {
            traverseMap((Map<String,Object>)obj);
        }
        else if (obj instanceof List<Object>)
        {
            traverseList((List<Object>)obj);
        }
        else
        {
            g.writeObject(obj);
        }
    }
    
    public String getAsString()
    {
        return g.getAsString();
    }
    
    void traverseMap(Map<String,Object> obj)
    {
        List<String> keys = new List<String>(obj.keySet());
        keys.sort();
        
        g.writeStartObject();
        for (String key : keys)
        {
            Object value = obj.get(key);
            g.writeFieldName(key + '_x'); // <------ reserved word safety here
            
            if (value == null)
            {
                g.writeNull();
            }
            else if (value instanceof Map<String,Object>)
            {
                traverseMap((Map<String,Object>)value);
            }
            else if (value instanceof List<Object>)
            {
                traverseList((List<Object>)value);
            }
            else
            {
                g.writeObject(value);
            }
        }
        g.writeEndObject();
    }
    
    void traverseList(List<Object> objs)
    {
        g.writeStartArray();
        for (Object obj : objs)
        {
            if (obj == null)
            {
                g.writeNull();
            }
            else if (obj instanceof Map<String,Object>)
            {
                traverseMap((Map<String,Object>)obj);
            }
            else if (obj instanceof List<Object>)
            {
                traverseList((List<Object>)obj);
            }
            else
            {
                g.writeObject(obj);
            }
        }
        g.writeEndArray();
    }
    
}
3

We have to change the Key of JSON for sure .Reserved words cant be keys .We will run into compile time errors

2
  • I am not actually in control of the JSON. I am receiving it from an external API. Oct 12, 2012 at 19:02
  • 3
    Catch the JSON and write an algorithm to append x in the key .Assuming there are only few keys you may change Oct 12, 2012 at 19:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .