Let's say I have a virtual class and two classes that extend it and I am getting a JSON response that could be either one of those classes. Is there a way to deserialize the JSON into the appropriate class without examining it manually?
Here are my classes:
public with sharing virtual class BaseClass
{
public Integer id;
}
public with sharing class foo extends BaseClass
{
public String foo;
}
public with sharing class bar extends BaseClass
{
public Integer bar;
}
I want to be able to do something like
Json.deserialize(jsonstring,Class.BaseClass.Descendants)
Attribution to: Greg Grinberg
Possible Suggestion/Solution #1
I don't think it is possible.
JSON is a data only object, and has no knowledge of your classes.
You would need to embed the name of your class and check that when de-serializing.
Attribution to: Bao-Long Nguyen-Trong
Possible Suggestion/Solution #2
If your JSON always has an id
, but also has either a foo
or a bar
, I think the corresponding Apex would be
public class Thing {
public Integer id;
public String foo;
public Integer bar;
}
You would deserialize into a Thing
, then test whether foo or bar was null. There's no way to persuade the parser to pick the correct subclass.
Attribution to: metadaddy
Possible Suggestion/Solution #3
Based on the responses above, it's just not possible using the JSON.deserialize method. You have to develop your own parser. I too had the same requirement, and here's what I came up with. I'm sure it can be improved, since it's essentially parsing the JSON string 3 times.
In my case, I needed to take a parent QueryResult class, parse the JSON response into the class, but the class had a member variable called 'records'. This records member variable was a list of virtual classes, extended by 1 of 10 subclasses.
I parsed out the "type" attribute of the first record, which indicates what the subclass is, then used Type.forName() to deserialize the particular JSON token I needed into the correctly typed array.
private static QueryResult parseQueryResult(String jsonStr){
QueryResult queryResult = (QueryResult)JSON.deserialize(jsonStr, ToolingAPI.QueryResult.class);
queryResult.records = getQueryResultRecords(jsonStr);
return queryResult;
}
/**
* Helper method for parsing the QueryResult response and determining
* which instance of QueryResultRecord to use
*/
private static List<QueryResultRecord> getQueryResultRecords(String jsonStr){
String recordType = getRecordType(jsonStr);
if(recordType != null){
JSONParser parser = JSON.createParser(jsonStr);
while (parser.nextToken() != null) {
if ((parser.getText() == 'records')) {
parser.nextToken();
return (List<QueryResultRecord>)parser.readValueAs(Type.forName('List<ToolingAPI.'+recordType+'>'));
}
}
}
return null;
}
/**
* Helper method for parsing type attribute from query result records in JSON response
*/
private static String getRecordType(String jsonStr){
JSONParser parser = JSON.createParser(jsonStr);
while (parser.nextToken() != null) {
if ((parser.getText() == 'records')) {
while(parser.nextToken() != null) {
if(parser.getText() == 'attributes'){
while(parser.nextToken() != null){
if(parser.getText() == 'type'){
//Value of type attribute
parser.nextToken();
return parser.getText();
}
}
}
}
}
}
return null;
}
Again, this can probably be improved on, but it gets the job done.
Attribution to: James Loghry
Possible Suggestion/Solution #4
How about using Json.deserializeStrict
to attempt each class? If your type
lacks the necessary fields it will throw a System.JsonException
which you can catch then attempt the next type?
eg:
List<Type> types = new List<Type>{
BaseClass.class,
Foo.class,
Bar.class
};
BaseClass value;
for (Type t : types) {
try {
value = (BaseClass)Json.deserializeStrict(jsonstring, t);
} catch (JsonException e) {
continue;
}
}
if (t instanceof Bar) ...
else if (t instanceof Foo) ...
else if (t instanceof BaseClass) ...
Attribution to: bigassforce
This content is remixed from stackoverflow or stackexchange. Please visit https://salesforce.stackexchange.com/questions/1373