Supposons que nous ayons ce JSON:Comment désérialiser des types génériques avec Moshi?
[
{
"__typename": "Car",
"id": "123",
"name": "Toyota Prius",
"numDoors": 4
},
{
"__typename": "Boat",
"id": "4567",
"name": "U.S.S. Constitution",
"propulsion": "SAIL"
}
]
(il pourrait y avoir beaucoup plus d'éléments à la liste, ce qui montre que deux)
Je Car
et Boat
POJO qui utilisent une classe de base Vehicle
pour la commune champs:
public abstract class Vehicle {
public final String id;
public final String name;
}
public class Car extends Vehicle {
public final Integer numDoors;
}
public class Boat extends Vehicle {
public final String propulsion;
}
Le résultat de l'analyse de cette JSON devrait être un List<Vehicle>
. Le problème est qu'aucun parseur JSON va savoir, hors de la boîte, que __typename
est de savoir comment distinguer un Boat
d'un Car
.
Avec Gson, je peux créer un JsonDeserializer<Vehicle>
qui peut examiner le champ __typename
, identifier si c'est un Car
ou Boat
, utilisez deserialize()
fourni sur le JsonDeserializationContext
pour analyser l'objet JSON particulier dans le type approprié. Cela fonctionne bien.
Cependant, la chose particulière que je construis devrait supporter les parseurs JSON enfichables, et j'ai pensé que j'essaierais Moshi comme un analyseur alternative. Cependant, ce problème particulier n'est pas bien couvert dans la documentation de Moshi à l'heure actuelle, et j'ai de la difficulté à trouver la meilleure façon d'y remédier. L'équivalent le plus proche de JsonDeserializer<T>
is JsonAdapter<T>
. Cependant, fromJson()
obtient passé un JsonReader
, qui a une API destructrice. Pour savoir ce que le __typename
est, je serais capable d'analyser tout à la main à partir des événements JsonReader
. Alors que je pourrais appeler adapter()
on the Moshi
instance pour essayer d'invoquer la logique d'analyse Moshi existante une fois que je connais le type de béton approprié, j'aurai consommé des données hors du JsonReader
et cassé sa capacité à fournir la description complète de l'objet plus. Un autre analogue de JsonDeserializer<Vehicle>
serait un @FromJson
-annotated method qui renvoie un Vehicle
. Cependant, je ne peux pas identifier une chose simple à passer dans la méthode. La seule chose que je peux penser est de créer encore une autre POJO représentant l'union de tous les domaines possibles:
public class SemiParsedKindOfVehicle {
public final String id;
public final String name;
public final Integer numDoors;
public final String propulsion;
public final String __typename;
}
Puis, en théorie, si je @FromJson Vehicle rideLikeTheWind(SemiParsedKindOfVehicle rawVehicle)
sur une classe que je me inscrire comme un adaptateur de type avec Moshi
, Moshi pourrait être en mesure d'analyser mes objets JSON en SemiParsedKindOfVehicle
instances et d'appeler rideLikeTheWind()
. Dans là, je rechercherais le __typename
, identifie le type, et construis complètement le Car
ou le Boat
moi-même, retournant cet objet. Bien que réalisable, c'est un peu plus complexe que l'approche Gson, et mon scénario Car
/Boat
est sur la fin simple des structures de données possibles que je vais devoir traiter.
Existe-t-il une autre approche pour gérer cela avec Moshi qui me manque?
La désérialisation polymorphique est maintenant beaucoup mieux supportée dans la version 1.4 de Moshi. (https://github.com/square/moshi/issues/89 pour les liens vers tous les détails.) Merci pour le post. Je vais suivre la balise Moshi sur SO maintenant, alors dépassez toutes les questions! –
@EricCochran: Merci pour l'info! Mais, à moins de mal interpréter les choses, cette question ne semble pas se rapporter à la désérialisation polymorphe. – CommonsWare
peut-être le plus important, 'JsonAdapter.fromJsonValue (Object)' (https://github.com/square/moshi/pull/234/files) permet de créer ces valeurs à partir de Maps et autres. –