Impossible de désérialiser JSON en tant que type abstrait
Lors de l'exécution d'un test qui désérialise une chaîne JSON, j'obtiens le message d'erreur:
Impossible de désérialiser JSON en tant que type abstrait: TestController.Item
Le JSON contient une liste d'objets qui est abstraite et il n'est pas possible de le désérialiser.
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);
}
}
}
Voici la classe de test:
@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) {
}
}
}
J'ai trouvé une demande d'extraction github avec la même erreur. Il y a une mise à jour critique et je suis sûr que cela cause le problème.
Avez-vous une idée de la façon de résoudre ce problème?
Réponses
Même lorsque ce code était compilé et exécuté, il avait un problème; vous ne pouvez pas réellement instancier un SomeItem dans la liste des éléments, car l'analyseur JSON ne saurait pas quel sous-type vous voulez réellement utiliser.
En tant que telle, la mise à jour critique est en fait une bonne chose, car elle corrige un comportement aberrant avec ce type de code. La solution est de créer manuellement les objets en mémoire.
Cela indique également un éventuel défaut de conception, si cela est destiné à être utilisé avec une sorte d'API qui utiliserait JSON. Cela ne fonctionnera tout simplement pas, car le système ne peut pas déterminer le type correct de sous-classe à utiliser pour le type de données, et dans un langage fortement typé comme Apex, c'est problématique.
Puisque tout est marqué TestVisible, la solution correcte serait de créer manuellement les objets:
TestController.SomeClass wrapper = new TestController.SomeClass();
wrapper.items = new TestController.SomeItem[0];
wrapper.items.add(new TestController.SomeItem());
// etc //
En résumé, il n'y a pas de solution de contournement directe. Vous devez être capable de désérialiser en une classe concrète. Si vous ne pouvez pas, alors vos options sont quelque chose comme la construction manuelle (ci-dessus), en utilisant JSONGenerator, ou en utilisant un objet générique (par exemple List<Map<String, Object>>
).
J'ai rencontré ce problème aujourd'hui en raison de l'activation de la mise à jour critique. J'ai pu le résoudre en changeant ma classe abstraite en classe virtuelle.