6

When running a test that's deserializing a JSON string, I get the error message:

Cannot deserialize JSON as abstract type: TestController.Item

The JSON contains a list of objects which is abstract and it's not possible to deserialize it.

public class TestController {
    
    @Testvisible
    private abstract class Item{
        public String bla;
        public String bla2;
    }


    public class SomeItem extends Item{
        private SomeItem() {
            bla = 'bla';
        }
    }


    public class SomeClass{

        @Testvisible private List<Item> items;

        public Graph(List<SomeItem> someItems) {
            items = new List<Item>();
            items.addAll((List<Item>) someItems);
        }
    }
}

This is the test class:

@IsTest
public class TestController_Test {
    
    @IsTest
    private static void testSomething() {
        TestController.SomeClass someClass = (TestController.SomeClass) System.JSON.deserialize(json, TestController.SomeClass.class);

        for(TestController.Item item : someClass.items) {

        }
    }       
}

I found a github pull request with the same error. There is a Critical Update and I'm sure that this causes the problem.

Do you have any idea how to solve that problem?

  • 1
    Could you post the json string value? – arut Aug 19 at 13:14
  • 1
    What is the purpose of private abstract class? – Santanu Boral Aug 19 at 13:15
5

Even when this code would compile and run, it had a problem; you couldn't actually instantiate a SomeItem inside the list of Item, because the JSON parser wouldn't know what subtype you actually mean to use.

As such, the Critical Update is actually a good thing, because it fixes aberrant behavior with this type of code. The solution is to manually create the objects in memory.

This also points to a possible design flaw, if this is meant to be used with some sort of API that would use JSON. It simply won't work, because the system cannot determine the correct type of subclass to use for the data type, and in a strongly typed language like Apex, that's problematic.

Since everything is marked TestVisible, the correct solution would be to manually create the objects:

TestController.SomeClass wrapper = new TestController.SomeClass();
wrapper.items = new TestController.SomeItem[0];
wrapper.items.add(new TestController.SomeItem());
// etc //

In summary, there is no direct workaround. You must be able to deserialize into a concrete class. If you can't, then your options are something like constructing manually (above), using JSONGenerator, or using a generic Object (e.g. List<Map<String, Object>>).

| improve this answer | |
2

I ran into this issue today due to the critical update activating. I was able to resolve it by changing my abstract class to a virtual class.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.