Find your content:

Search form

You are here

How do you deserialize json into the correct virtual class?

 
Share

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

My Block Status

My Block Content