C'est une excellente question car elle permet d'isoler quelque chose qui devrait être facile mais qui nécessite en fait beaucoup de code.
Pour commencer, écrivez un résumé TypeAdapterFactory
qui vous donne des crochets pour modifier les données sortantes. Cet exemple utilise une nouvelle API dans Gson 2.2 appelée getDelegateAdapter()
qui vous permet de rechercher l'adaptateur que Gson utilise par défaut. Les adaptateurs de délégués sont extrêmement pratiques si vous souhaitez simplement modifier le comportement standard. Et contrairement aux adaptateurs de types personnalisés complets, ils seront mis à jour automatiquement lorsque vous ajouterez ou supprimerez des champs.
public abstract class CustomizedTypeAdapterFactory<C>
implements TypeAdapterFactory {
private final Class<C> customizedClass;
public CustomizedTypeAdapterFactory(Class<C> customizedClass) {
this.customizedClass = customizedClass;
}
@SuppressWarnings("unchecked") // we use a runtime check to guarantee that 'C' and 'T' are equal
public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
return type.getRawType() == customizedClass
? (TypeAdapter<T>) customizeMyClassAdapter(gson, (TypeToken<C>) type)
: null;
}
private TypeAdapter<C> customizeMyClassAdapter(Gson gson, TypeToken<C> type) {
final TypeAdapter<C> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<C>() {
@Override public void write(JsonWriter out, C value) throws IOException {
JsonElement tree = delegate.toJsonTree(value);
beforeWrite(value, tree);
elementAdapter.write(out, tree);
}
@Override public C read(JsonReader in) throws IOException {
JsonElement tree = elementAdapter.read(in);
afterRead(tree);
return delegate.fromJsonTree(tree);
}
};
}
/**
* Override this to muck with {@code toSerialize} before it is written to
* the outgoing JSON stream.
*/
protected void beforeWrite(C source, JsonElement toSerialize) {
}
/**
* Override this to muck with {@code deserialized} before it parsed into
* the application type.
*/
protected void afterRead(JsonElement deserialized) {
}
}
La classe ci-dessus utilise la sérialisation par défaut pour obtenir un arbre JSON (représenté par JsonElement
), puis appelle la méthode hook beforeWrite()
pour permettre à la sous-classe de personnaliser cet arbre. De même pour la désérialisation avec afterRead()
.
Ensuite, nous le sous-classons pour le MyClass
exemple. Pour illustrer, je vais ajouter une propriété synthétique appelée "size" à la carte lorsqu'elle est sérialisée. Et par symétrie, je la supprime lorsqu'elle est désérialisée. En pratique, cela peut être n'importe quelle personnalisation.
private class MyClassTypeAdapterFactory extends CustomizedTypeAdapterFactory<MyClass> {
private MyClassTypeAdapterFactory() {
super(MyClass.class);
}
@Override protected void beforeWrite(MyClass source, JsonElement toSerialize) {
JsonObject custom = toSerialize.getAsJsonObject().get("custom").getAsJsonObject();
custom.add("size", new JsonPrimitive(custom.entrySet().size()));
}
@Override protected void afterRead(JsonElement deserialized) {
JsonObject custom = deserialized.getAsJsonObject().get("custom").getAsJsonObject();
custom.remove("size");
}
}
Enfin, pour mettre tout cela en place, créez un Gson
qui utilise le nouvel adaptateur de type :
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new MyClassTypeAdapterFactory())
.create();
La nouvelle de Gson Adaptateur de type y TypeAdapterFactory Les types sont extrêmement puissants, mais ils sont aussi abstraits et il faut de la pratique pour les utiliser efficacement. Nous espérons que cet exemple vous sera utile !