J'ai une liste d'objets que je dois désérialiser dans leurs sous-classes appropriées. J'ai pu faire fonctionner cela en construisant un objet gson comme ci-dessous

    JsonDeserializer<Shape> jdTypes = (json, typeOfT, jsonContext) -> {
        JsonObject jsonObject = json.getAsJsonObject();
        int type = jsonObject.get("type").getAsInt();

        Gson gson = new Gson();

        switch (type){
            case Types.TYPE_CARD:
                return gson.fromJson(json, Card.class);

            case Types.TYPE_BLOCK:
                return gson.fromJson(json, Block.class);

            case Types.TYPE_STRIPE:
                return gson.fromJson(json, Stripe.class);

            //...

        }
    };

    GsonBuilder builder = new GsonBuilder();
    builder.registerTypeAdapter(Shape.class, jdTypes);
    builder.create();

Bien que cela fonctionne, il doit y avoir une meilleure façon de le faire. Actuellement, je crée une instance de Gson dans mon GsonBuilder. Yikes. Existe-t-il un moyen d'utiliser l'objet Gson que je suis sur le point de créer pour désérialiser les différents sous-types?

0
Canoe 1 avril 2017 à 20:07

2 réponses

Meilleure réponse

Si je comprends bien ce problème, vous pouvez simplement utiliser le JsonDeserializationContext directement.

return jsonContext.deserialize(json, classOfType(type));

classOfType() doit simplement renvoyer la classe correspondant au type de votre forme.

1
tynn 1 avril 2017 à 18:17

Pas besoin de réinventer la roue. Votre problème est classique dans le cadre de Gson, et il existe un RuntimeTypeAdapterFactory qui est conçu pour résoudre des problèmes exactement de ce type. Par exemple, si vous avez quelque chose comme ceci:

final class Types {

    private Types() {
    }

    static final int TYPE_CARD = 1;
    static final int TYPE_BLOCK = 2;
    static final int TYPE_STRIPE = 3;

    abstract static class Abstract {

        private Abstract() { }

        static final class Card extends Abstract { }    
        static final class Block extends Abstract { }   
        static final class Stripe extends Abstract {}

    }

}

Vous pouvez configurer et utiliser RuntimeTypeAdapterFactory:

private static final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(
                RuntimeTypeAdapterFactory.of(Types.Abstract.class, "type")
                        .registerSubtype(Types.Abstract.Card.class, String.valueOf(Types.TYPE_CARD))
                        .registerSubtype(Types.Abstract.Block.class, String.valueOf(Types.TYPE_BLOCK))
                        .registerSubtype(Types.Abstract.Stripe.class, String.valueOf(Types.TYPE_STRIPE))
        )
        .create();

Alors vous n'avez même besoin de rien d'autre:

private static final Type abstractListType = new TypeToken<List<Types.Abstract>>() {
}.getType();

public static void main(final String... args) {
    gson.<List<Types.Abstract>>fromJson("[{\"type\":1},{\"type\":2},{\"type\":3}]", abstractListType)
            .stream()
            .map(Types.Abstract::getClass)
            .forEach(System.out::println);
}

Production:

class q43159721.Types $ Carte $ Résumé
class q43159721.Types $ Abstract $ Block
class q43159721.Types $ Résumé $ Stripe

1
Lyubomyr Shaydariv 1 avril 2017 à 19:12