Im deserializing a JSON (that has both reserved apex words and other uncompilable strings having 'organizer__
' and '__c
' ) in Apex.
Ive been using a compiled class-based deserialization approach and the mogrifyJSON approach described in https://blog.deadlypenguin.com/2016/06/21/parse-json-reserved-words/ which works great when you have a known/finite number of illegal strings to mogrify/replace/handle, such as Apex reserved words, but im realizing my JSON payload ALSO has a dynamic number of other illegal strings (such as those having __c
or organizer__
in them). My approach is inadequate because apex is compiled and i cannot dynamically compile inner Public strings under cls_orderby
and in replacements
to represent/accommodate the reality of dynamic json having endless possibilities of __c
and organizer__
strings.
Objective:
I need help reaching debug statement parity without having to hardcode inner Public String
member variables under cls_orderby
and in replacements
.
Debugging my inadequate deserialization approach (compiled class structure /typed variables structure approach) is easy, but wont scale. See it below. Assume this debug was generated after i ran my JSON through queryParamJSONDeserializer obj = queryParamJSONDeserializer.parse(jsonString);
Furthermore the getType(o)
method used in debugging was modeled after https://ideas.salesforce.com/s/idea/a0B8W00000GdY4zUAF/method-to-get-the-type-of-an-object-also-primitive-type-not-only-sobject
USER_DEBUG|[1125]|DEBUG|obj.where_Z.organizer_Sprint_c.eq: a0D3u00000AdbJIEAZ getType: Id
USER_DEBUG|[1126]|DEBUG|obj.where_Z.organizer_ussp_Nested_Under_c: null getType:
USER_DEBUG|[1129]|DEBUG|obj.orderby[0].organizer_SortOrder_c: ASC getType: String
USER_DEBUG|[1130]|DEBUG|obj.orderby[0].organizer_sortorder_approved_c: null getType:
USER_DEBUG|[1131]|DEBUG|obj.orderby[0].organizer_sortorder_assignees_c: null getType:
USER_DEBUG|[1132]|DEBUG|obj.orderby[0].organizer_sortorder_recalled_c: null getType:
USER_DEBUG|[1133]|DEBUG|obj.orderby[0].organizer_sortorder_rejected_c: null getType:
USER_DEBUG|[1134]|DEBUG|obj.orderby[0].organizer_sortorder_submitted_c: null getType:
USER_DEBUG|[1182]|DEBUG|obj.limit_Z: 15 getType: Integer
The inadequate approach code:
//Generated by AdminBooster and manually changed to incorporate handling reserved or other uncompilable strings via the mogrifyJSON approach written about in https://blog.deadlypenguin.com/2016/06/21/parse-json-reserved-words/
// USAGE
//Note1 queryParamJSONDeserializer does deserialize this, but is inadequate because its using hardcoded tactics.
//Note2 In reality the json string can contain many more __c suffixed strings and im exploring how to pivot into a more general design that avoids hardcoding.
/*
String jsonInput= '{'+
' "where": {'+
' "organizer__Sprint__c": {'+
' "eq": "a0D3u00000AdbJIEAZ"'+
' },'+
' "organizer__ussp_Nested_Under__c": {'+
' "eq": ""'+
' }'+
' },'+
' "orderby": [{'+
' "organizer__SortOrder__c": "ASC"'+
' }],'+
' "limit": 15'+
'}';
queryParamJSONDeserializer obj = queryParamJSONDeserializer.parse(jsonString);
System.debug('obj.where_Z.organizer_Sprint_c:' + obj.where_Z.organizer_Sprint_c);
*/
public class queryParamJSONDeserializer{
public cls_where Where_Z;
public cls_orderby[] orderby;
public Integer limit_Z; //15
public class cls_where {
public cls_organizer_Sprint_c organizer_Sprint_c;
public cls_organizer_ussp_Nested_Under_c organizer_ussp_Nested_Under_c;
}
public class cls_organizer_Sprint_c {
public String eq;
public String nin;
public String ne;
public String gte;
public String lte;
}
public class cls_organizer_ussp_Nested_Under_c {
public String eq;
public String nin;
public String ne;
public String gte;
public String lte;
}
public class cls_orderby {
public String organizer_SortOrder_c; //works but inadequate because i cannot sustain hardcoding these public Strings
public String organizer_sortorder_approved_c; //works but inadequate because i cannot sustain hardcoding these public Strings
public String organizer_sortorder_assignees_c; //works but inadequate because i cannot sustain hardcoding these public Strings
public String organizer_sortorder_recalled_c; //works but inadequate because i cannot sustain hardcoding these public Strings
public String organizer_sortorder_rejected_c; //works but inadequate because i cannot sustain hardcoding these public Strings
public String organizer_sortorder_submitted_c; //works but inadequate because i cannot sustain hardcoding these public Strings
}
public static queryParamJSONDeserializer parse(String stringifydQueryParam){
return (queryParamJSONDeserializer) System.JSON.deserialize(mogrifyJSON(stringifydQueryParam), queryParamJSONDeserializer.class);
}
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> {
'limit' => 'limit_Z',
'where' => 'Where_Z',
'organizer__SortOrder__c' => 'organizer_SortOrder_c',
'organizer__sortorder_approved__c' => 'organizer_sortorder_approved_c',
'organizer__sortorder_assignees__c' => 'organizer_sortorder_assignees_c',
'organizer__sortorder_recalled__c' => 'organizer_sortorder_recalled_c',
'organizer__sortorder_rejected__c' => 'organizer_sortorder_rejected_c',
'organizer__sortorder_submitted__c' => 'organizer_sortorder_submitted_c',
'organizer__ussp_Nested_Under__c' => 'organizer_ussp_Nested_Under_c',
'organizer__Sprint__c' => 'organizer_Sprint_c'
};
// 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;
}
}
Questions and thinking about next steps:
How could i reach debug statement parity (similar debug statement results) without using class-based deserialization, and avoiding hardcoding fieldnames__c in cls_orderby
and replacements
? Im looking into deserializeUntyped
instead of a compiled-class-structure because my inadequate class-based deserialization wont scale to endless fieldnames in my json.