4 votes

Adaptateur de type Gson AutoValue pour une Map<String, List<Obj>>.

J'essaie de trouver un moyen d'utiliser AutoValue pour désérialiser un objet JSON en une classe Java (qui est également Parcelable)

La réponse JSON se présente généralement sous la forme de,

{
  "varKey1": {
    "occupations": [
      {
        "value": "val1",
        "name": "name1"
      },
      {
        "value": "val2",
        "name": "name2"
      }
    ]
  },
  "varKey2": {
    "occupations": [
      {
        "value": "val1",
        "name": "name1"
      },
      {
        "value": "val2",
        "name": "name2"
      }
    ]
  }
}

varKey1 y varKey2 sont des chaînes qui ne sont pas fixes/prédéfinies et qui peuvent donc avoir n'importe quelle valeur.

J'ai du mal à comprendre à quoi devrait ressembler le typeAdapter avec AutoValue Gson, et toute aide à ce sujet serait grandement appréciée.

5voto

Lyubomyr Shaydariv Points 1703

D'après ce que j'ai compris, AutoValue et AutoValue : Gson Extension fonctionnent, vous ne pouvez pas désérialiser Map<String, List<Obj>> à partir du JSON donné en utilisant uniquement ces outils, puisqu'ils ne sont que de simples générateurs de code source. Ce dernier indique même qui crée un simple Gson TypeAdapterFactory pour chaque objet annoté AutoValue .

Prend le JSON donné et Map<String, List<Obj>> vous pouvez le faire :

  • ... ont Map<String, Wrapper> où la classe enveloppante contient List<Obj> (et déclarer un adaptateur de type pour chaque @AutoValue -annotée), ce qui permet d'avoir un objet médiateur intermédiaire muet nommé Wrapper .
  • ... mettre en œuvre un adaptateur de type personnalisé qui ne peut pas être généré par AutoValue : Gson Extension.

La deuxième option n'est pas aussi difficile qu'il n'y paraît et peut aider à contourner les cas où l'extension AutoValue ne peut pas générer d'adaptateurs de type pour d'autres cas sophistiqués.

Déclarer les jetons de type nécessaires

final class TypeTokens {

    private TypeTokens() {
    }

    static final TypeToken<Map<String, List<Obj>>> mapStringToObjListTypeToken = new TypeToken<Map<String, List<Obj>>>() {
    };

    static final TypeToken<List<Obj>> objListTypeTypeToken = new TypeToken<List<Obj>>() {
    };

}

Implémentation d'une fabrique d'adaptateurs de types personnalisés

Les usines d'adaptateurs de type Gson sont utilisées pour résoudre (et lier) un adaptateur de type particulier et, si nécessaire, lier l'adaptateur à des instances Gson pour gérer toute configuration Gson.

final class CustomTypeAdapterFactory
        implements TypeAdapterFactory {

    private static final TypeAdapterFactory customTypeAdapterFactory = new CustomTypeAdapterFactory();

    static TypeAdapterFactory getCustomTypeAdapterFactory() {
        return customTypeAdapterFactory;
    }

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        if ( typeToken.getType().equals(mapStringToObjListTypeToken.getType()) ) {
            @SuppressWarnings({ "unchecked", "rawtypes" })
            final TypeAdapter<T> castTypeAdapter = (TypeAdapter) getMapStringToObjectListTypeAdapter(gson);
            return castTypeAdapter;
        }
        return null;
    }

}

Notez que la seule responsabilité de la fabrique est de renvoyer un adaptateur de type spécial qui peut ignorer les wrappers dans votre JSON. Si un autre type est demandé à la fabrique, le retour de null est sûr et permet à Gson d'essayer de choisir le meilleur adaptateur d'un autre type (intégré ou de votre configuration).

Mettre en œuvre l'adaptateur de type

C'est en fait ce que fait Auto Value : Gson Extension ne semble pas pouvoir faire. Comme les adaptateurs de type sont essentiellement orientés vers le flux, ils peuvent sembler de trop bas niveau, mais c'est le mieux que Gson puisse faire parce que le flux est une technique très efficace, et il est également utilisé dans ce que Auto Value : Gson Extension génère.

final class MapStringToObjectListTypeAdapter
        extends TypeAdapter<Map<String, List<Obj>>> {

    private final TypeAdapter<List<Obj>> wrapperAdapter;

    private MapStringToObjectListTypeAdapter(final TypeAdapter<List<Obj>> wrapperAdapter) {
        this.wrapperAdapter = wrapperAdapter;
    }

    static TypeAdapter<Map<String, List<Obj>>> getMapStringToObjectListTypeAdapter(final Gson gson) {
        return new MapStringToObjectListTypeAdapter(gson.getAdapter(objListTypeTypeToken));
    }

    @Override
    @SuppressWarnings("resource")
    public void write(final JsonWriter out, final Map<String, List<Obj>> value)
            throws IOException {
        if ( value == null ) {
            // nulls must be written
            out.nullValue();
        } else {
            out.beginObject();
            for ( final Entry<String, List<Obj>> e : value.entrySet() ) {
                out.name(e.getKey());
                out.beginObject();
                out.name("occupations");
                wrapperAdapter.write(out, e.getValue());
                out.endObject();
            }
            out.endObject();
        }
    }

    @Override
    public Map<String, List<Obj>> read(final JsonReader in)
            throws IOException {
        // if there's JSON null, then just return nothing
        if ( in.peek() == NULL ) {
            return null;
        }
        // or read the map
        final Map<String, List<Obj>> result = new LinkedHashMap<>();
        // expect the { token
        in.beginObject();
        // and read recursively until } is occurred
        while ( in.peek() != END_OBJECT ) {
            // this is the top-most level where varKey# occur
            final String key = in.nextName();
            in.beginObject();
            while ( in.peek() != END_OBJECT ) {
                final String wrapperName = in.nextName();
                switch ( wrapperName ) {
                case "occupations":
                    // if this is the "occupations" property, delegate the parsing to an underlying type adapter
                    result.put(key, wrapperAdapter.read(in));
                    break;
                default:
                    // or just skip the value (or throw an exception, up to you)
                    in.skipValue();
                    break;
                }
            }
            in.endObject();
        }
        in.endObject();
        return result;
    }

}

Auto Usine générée par la valeur

Contrairement à la fabrique d'adaptateurs de types personnalisés, certaines fabriques d'adaptateurs de types Gson sont déjà générées et peuvent traiter les éléments suivants abstract classes comme ce que Obj (au moins dans votre code source, pas dans celui qui est généré).

@GsonTypeAdapterFactory
abstract class GeneratedTypeAdapterFactory
        implements TypeAdapterFactory {

    public static TypeAdapterFactory getGeneratedTypeAdapterFactory() {
        return new AutoValueGson_GeneratedTypeAdapterFactory();
    }

}

Comment l'utiliser

private static void dump(final Map<?, ?> map) {
    for ( final Entry<?, ?> e : map.entrySet() ) {
        out.print(e.getKey());
        out.print(" => ");
        out.println(e.getValue());
    }
}

...

final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(getGeneratedTypeAdapterFactory())
        .registerTypeAdapterFactory(getCustomTypeAdapterFactory())
        .create();
dump(gson.fromJson(json, mapStringToObjListTypeToken.getType()));

donne le résultat suivant :

varKey1 => [Obj{nom=nom1, valeur=val1}, Obj{nom=nom2, valeur=val2}]
varKey2 => [Obj{nom=nom1, valeur=val1}, Obj{nom=nom2, valeur=val2}]

Si vous n'utilisez que les wrappers de la première option, le résultat est le suivant :

varKey1 => Wrapper{occupations=[Obj{nom=nom1, valeur=val1}, Obj{nom=nom2, valeur=val2}]}
varKey2 => Wrapper{occupations=[Obj{nom=nom1, valeur=val1}, Obj{nom=nom2, valeur=val2}]}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X