Impossibile deserializzare JSON come tipo astratto
Quando eseguo un test che deserializza una stringa JSON, ricevo il messaggio di errore:
Impossibile deserializzare JSON come tipo astratto: TestController.Item
Il JSON contiene un elenco di oggetti che è astratto e non è possibile deserializzarlo.
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);
}
}
}
Questa è la classe di prova:
@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) {
}
}
}
Ho trovato una richiesta pull github con lo stesso errore. C'è un aggiornamento critico e sono sicuro che questo causi il problema.
Hai idea di come risolvere il problema?
Risposte
Anche quando questo codice veniva compilato ed eseguito, aveva un problema; non è possibile creare un'istanza di SomeItem nell'elenco di Item, perché il parser JSON non saprebbe quale sottotipo si intende effettivamente utilizzare.
In quanto tale, l'aggiornamento critico è effettivamente una buona cosa, perché corregge il comportamento anomalo con questo tipo di codice. La soluzione è creare manualmente gli oggetti in memoria.
Questo indica anche un possibile difetto di progettazione, se questo è pensato per essere utilizzato con una sorta di API che utilizzerebbe JSON. Semplicemente non funzionerà, perché il sistema non può determinare il tipo corretto di sottoclasse da utilizzare per il tipo di dati, e in un linguaggio fortemente tipizzato come Apex, ciò è problematico.
Poiché tutto è contrassegnato come TestVisible, la soluzione corretta sarebbe creare manualmente gli oggetti:
TestController.SomeClass wrapper = new TestController.SomeClass();
wrapper.items = new TestController.SomeItem[0];
wrapper.items.add(new TestController.SomeItem());
// etc //
In sintesi, non esiste una soluzione diretta. Devi essere in grado di deserializzare in una classe concreta. Se non puoi, le tue opzioni sono qualcosa come costruire manualmente (sopra), usando JSONGenerator o usando un oggetto generico (ad esempio List<Map<String, Object>>
).
Oggi ho riscontrato questo problema a causa dell'attivazione dell'aggiornamento critico. Sono stato in grado di risolverlo cambiando la mia classe astratta in una classe virtuale.