Find your content:

Search form

You are here

How do you deserialize json properties that are reserved words in apex?

 
Share

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.


Attribution to: Greg Grinberg

Possible Suggestion/Solution #1

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.


Attribution to: Keith C

Possible Suggestion/Solution #2

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


Attribution to: Mohith Shrivastava

Possible Suggestion/Solution #3

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


Attribution to: Daniel Blackhall

Possible Suggestion/Solution #4

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();
    }

}
/**
 * The MIT License (MIT)
 * 
 * Copyright (c) 2015 bigass.force.com
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

Attribution to: bigassforce

Possible Suggestion/Solution #5

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 (charlie@callawaycloudconsulting.com)
 * 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


Attribution to: NSjonas
This content is remixed from stackoverflow or stackexchange. Please visit https://salesforce.stackexchange.com/questions/2276

My Block Status

My Block Content