Bug? Why doesn’t JSON.serialize support DescribeSObjectResult?

JSON.serialize supports DescribeFieldResult, meaning that I can do this:

return JSON.serialize(Account.Name.getDescribe());

However, when I try to serialize the SObject itself, I experience an exception:

return JSON.serialize(Schema.Account.SObjectType.getDescribe());

This throws the following exception:

System.JSONException: (was java.lang.NullPointerException) (through reference chain: common.api.soap.wsdl.DescribeSObjectResult["listviewable"])

Oddly, ListViewable is not in the Apex Code Developer’s Guide or the SOAP API Developer’s Guide, so I presume it’s leaking over from the REST API based on the limited information I could find on Google about this (the documentation doesn’t state that this property exists, but I found a pastebin that referenced this property (and it was nil), but it doesn’t appear anywhere else.

Answer

My Result and Possible “Fix”. The above code works fine in my org (na11), i get “listviewable”: null in my JSON, as well as lookupLayoutable which is also not documetned. I’m executing it from Execute Annoynmous. As a possible “fix” (see below for my thoughts on more durable solution), it maybe sensitive to API version of your calling Apex class, so check your metadata XML file for the latest version.

My Experience and Possible Explanation… Though in general I’ve personally found serialising and deserialising (more so) system data types a bit hit and miss, certainly deserialising them. If you take the JSON.serialize method documentation literally, it suggests it really only designed for Apex types…

Serializes Apex objects into JSON content.

The general JSON documentation here, also states this…

JSON serialization and deserialization support is available for sObjects (standard objects and custom objects), Apex primitive and collection types, return types of Database methods (such as SaveResult, DeleteResult, and so on), and instances of your Apex classes.

So I suspect the reason it doesnt work is some bug (?) with how its discovering fields for platform types vs Apex types, as ‘private’ members would be ignored in Apex classes. It may be worth raising a Salesforce support case to get them to confirm this, though they may simply quote the above to you.

In Galaxy Far Far Away… This field does seem to occur quite a way back, I found it in a partner WSDL version 8.0 (circa 2008), here, but not in the latest. So I suspect its leaking out as a ‘private’ internal field left over from a long forgotten time. Comparing with latest Partner WSDL, lookupLayoutable also falls into this category.

Alternative ‘Custom Marhsalling’ Approach… In the meantime, the main thing i can suggest is leverage the JSONGenerator methods to marshall out your own similar JSON data structure, filtering out the troublesome bits. Its more work, but will be more stable i feel. You can use a mixture of writeXXXX (where XXX is the data type) and writeObject method if you want to chance your luck with those that appear to work (such as DescribeFieldResult). Sample from the docs is here, showing how to mix these.

public class JSONGeneratorSample{

    public class A { 
        String str;

        public A(String s) { str = s; }
    }

    static void generateJSONContent() {
        // Create a JSONGenerator object.
        // Pass true to the constructor for pretty print formatting.
        JSONGenerator gen = JSON.createGenerator(true);

        // Create a list of integers to write to the JSON string.
        List<integer> intlist = new List<integer>();
        intlist.add(1);
        intlist.add(2);
        intlist.add(3);

        // Create an object to write to the JSON string.
        A x = new A('X');

        // Write data to the JSON string.
        gen.writeStartObject();
        gen.writeNumberField('abc', 1.21);
        gen.writeStringField('def', 'xyz');
        gen.writeFieldName('ghi');
        gen.writeStartObject();

        gen.writeObjectField('aaa', intlist);

        gen.writeEndObject();

        gen.writeFieldName('Object A');

        gen.writeObject(x);

        gen.writeEndObject();

        // Get the JSON string.
        String pretty = gen.getAsString();

        System.assertEquals('{\n' +
        '  "abc" : 1.21,\n' +
        '  "def" : "xyz",\n' +
        '  "ghi" : {\n' +
        '    "aaa" : [ 1, 2, 3 ]\n' +
        '  },\n' +
        '  "Object A" : {\n' +
        '    "str" : "X"\n' +
        '  }\n' +
        '}', pretty);
    }
}

In Summary…

I’ve found serialising system data types a bit hit and miss, any time i get system errors from something as seemingly innocent as this and the docs seem to state otherwise, i’d personally step away and look for another solution that is more robust. Thus if i do then get errors I can confidently call bug. As final thought depending on who the ‘client’ is receiving this JSON, you may want to have it call the Salesforce REST or Partner API describe functionality directly perhaps, obviously thats more of general point here since I don’t know what your use case is.

Anyway, thats pretty much all I can think on this, it is a bit frustrating to have to consider the above for a data type that is likely exactly fit for your purpose. Hopefully the above has given you some further help, insight and next steps to consider… 🙂

Attribution
Source : Link , Question Author : sfdcfox , Answer Author : Andrew Fawcett

Leave a Comment