Невозможно десериализовать JSON как абстрактный тип
При запуске теста десериализации строки JSON я получаю сообщение об ошибке:
Невозможно десериализовать JSON как абстрактный тип: TestController.Item
JSON содержит список объектов, который является абстрактным и десериализовать его невозможно.
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);
}
}
}
Это тестовый класс:
@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) {
}
}
}
Я нашел запрос на перенос на github с той же ошибкой. Есть критическое обновление, и я уверен, что это вызывает проблему.
Вы знаете, как решить эту проблему?
Ответы
Даже когда этот код компилировался и запускался, у него была проблема; вы не можете создать экземпляр SomeItem внутри списка Item, потому что парсер JSON не будет знать, какой подтип вы на самом деле хотите использовать.
По сути, критическое обновление - это действительно хорошо, потому что оно исправляет аномальное поведение с этим типом кода. Решение состоит в том, чтобы вручную создавать объекты в памяти.
Это также указывает на возможный недостаток дизайна, если он предназначен для использования с каким-либо API, который будет использовать JSON. Это просто не сработает, потому что система не может определить правильный тип подкласса для использования для данного типа данных, а на строго типизированном языке, таком как Apex, это проблематично.
Поскольку все помечено как TestVisible, правильным решением было бы вручную создать объекты:
TestController.SomeClass wrapper = new TestController.SomeClass();
wrapper.items = new TestController.SomeItem[0];
wrapper.items.add(new TestController.SomeItem());
// etc //
Таким образом, нет прямого обходного пути. Вы должны иметь возможность десериализоваться в конкретный класс. Если вы не можете, тогда ваши варианты похожи на построение вручную (см. Выше), с использованием JSONGenerator или с использованием общего объекта (например List<Map<String, Object>>
).
Я столкнулся с этой проблемой сегодня из-за активации критического обновления. Я смог решить эту проблему, изменив свой абстрактный класс на виртуальный.