C'est une excellente question car elle isole 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 utiliserait par défaut. Les adaptateurs délégués sont extrêmement pratiques si vous souhaitez simplement modifier le comportement standard. Et contrairement aux adaptateurs de type personnalisés complets, ils resteront à jour automatiquement lorsque vous ajoutez et supprimez 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 une arborescence JSON (représentée par JsonElement
), puis appelle la méthode hook beforeWrite()
pour permettre à la sous-classe de personnaliser cette arborescence. De même pour la désérialisation avec afterRead()
.
Ensuite, nous sous-classons ceci pour l' MyClass
exemple spécifique . Pour illustrer, j'ajouterai une propriété synthétique appelée «taille» à la carte lorsqu'elle est sérialisée. Et pour la symétrie, je le supprimerai lorsqu'il sera désérialisé. En pratique, cela pourrait ê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, rassemblez le tout en créant une Gson
instance personnalisée qui utilise le nouvel adaptateur de type:
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new MyClassTypeAdapterFactory())
.create();
Les nouveaux types TypeAdapter et TypeAdapterFactory de Gson sont extrêmement puissants, mais ils sont également abstraits et nécessitent de la pratique pour être utilisés efficacement. J'espère que vous trouverez cet exemple utile!