How do you deserialize json properties that are reserved words in apex?
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.
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":');
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
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
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.
*/