Não é possível desserializar JSON como tipo abstrato

Aug 19 2020

Ao executar um teste que está desserializando uma string JSON, recebo a mensagem de erro:

Não é possível desserializar JSON como tipo abstrato: TestController.Item

O JSON contém uma lista de objetos que é abstrata e não é possível desserializar.

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);
        }
    }
}

Esta é a classe de teste:

@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) {

        }
    }       
}

Encontrei uma solicitação de pull do github com o mesmo erro. Há uma atualização crítica e tenho certeza de que isso causa o problema.

Tem ideia de como resolver esse problema?

Respostas

5 sfdcfox Aug 19 2020 at 22:44

Mesmo quando esse código era compilado e executado, ele apresentava um problema; você não poderia realmente instanciar um SomeItem dentro da lista de Item, porque o analisador JSON não saberia qual subtipo você realmente pretende usar.

Como tal, a atualização crítica é realmente uma coisa boa, porque corrige o comportamento anormal com este tipo de código. A solução é criar manualmente os objetos na memória.

Isso também aponta para uma possível falha de design, se for para ser usado com algum tipo de API que usaria JSON. Simplesmente não funcionará, porque o sistema não pode determinar o tipo correto de subclasse a ser usado para o tipo de dados, e em uma linguagem fortemente tipada como o Apex, isso é problemático.

Como tudo está marcado como TestVisible, a solução correta seria criar manualmente os objetos:

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

Em resumo, não há solução alternativa direta. Você deve ser capaz de desserializar em uma classe concreta. Se você não puder, então suas opções são algo como construir manualmente (acima), usando JSONGenerator ou usando um objeto genérico (por exemplo List<Map<String, Object>>).

2 RobAlexander Aug 22 2020 at 04:40

Encontrei esse problema hoje devido à ativação da atualização crítica. Consegui resolver isso mudando minha classe abstrata para uma classe virtual.