Gson, Dynamic InstanceCreator

For example, there are two classes

class A {
    public int key;
    public B b;
}

class B {
    private final int key;

    public int x;
    public int y;

    public B(int key) {
        this.key = key;
    }
}

So the field inner instance of B must be exactly like a field external instance A.

Instance Creator B:

public class B_InstanceCreator implements InstanceCreator<B> {
    private final int key;

    public B_InstanceCreator(int key) {
        this.key = key;
    }

    @Override
    public B createInstance(Type type) {
        return new B(key);
    }
}

How can I implement a type adapter for A that creates (and then uses to deserialize the internal B) B_InstanceCreator immediately after retrieving the key ?

+4
source share
1 answer

-, , , . InstanceCreator, , , . key A, . A ( , , @SerializedName, @Expose, @JsonAdapter...):

final class AJsonDeserializer
        implements JsonDeserializer<A> {

    private static final JsonDeserializer<A> aJsonDeserializer = new AJsonDeserializer();

    private AJsonDeserializer() {
    }

    static JsonDeserializer<A> getAJsonDeserializer() {
        return aJsonDeserializer;
    }

    @Override
    public A deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context) {
        final JsonObject root = jsonElement.getAsJsonObject();
        final A a = new A();
        a.key = root.get("key").getAsJsonPrimitive().getAsInt();
        a.b = createInnerGson(a.key).fromJson(root.get("b"), B.class);
        return a;
    }

    private static Gson createInnerGson(final int key) {
        return new GsonBuilder()
                .registerTypeAdapter(B.class, (InstanceCreator<B>) type -> new B(key))
                .create();
    }

}

, - createInnerGson, Gson Builder, , , Gson ( , ). key . ( , Gson fromJson B (, - gson.merge(someJson, someBInstance))), Gson .)

Deserializer JSON ( , A B , toString):

final Gson gson = new GsonBuilder()
        .registerTypeAdapter(A.class, getAJsonDeserializer())
        .create();
final A a = gson.fromJson("{\"key\":2048,\"b\":{\"x\":20,\"y\":30}}", A.class);
out.printf("%d, %d\n", a.key, a.b.key);
out.println(a);

:

2048, 2048
A{key=2048, b=B{key=2048, x=20, y=30}}

V2

, ... JsonDeserializer<A> , , A. , "" , Gson. , , , , Gson " " , (), , , (. Gson ).

GsonFactoryJsonDeserializer.java

. Gson . factory: JsonElement Gson, , JsonElement Gson factory (, InstanceCreator, ?). , JSON.

final class GsonFactoryJsonDeserializer
        implements JsonDeserializer<Object> {

    private final Function<? super JsonElement, Gson> gsonFactory;

    private GsonFactoryJsonDeserializer(final Function<? super JsonElement, Gson> gsonFactory) {
        this.gsonFactory = gsonFactory;
    }

    static JsonDeserializer<Object> gsonFactoryJsonDeserializer(final Function<? super JsonElement, Gson> gsonFactory) {
        return new GsonFactoryJsonDeserializer(gsonFactory);
    }

    static <K> JsonDeserializer<Object> gsonFactoryJsonDeserializer(final Function<? super JsonElement, ? extends K> jsonElementToKey,
            final Function<? super K, Gson> keyToGson) {
        return new GsonFactoryJsonDeserializer(jsonElementToKey.andThen(keyToGson));
    }

    @Override
    public Object deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
            throws JsonParseException {
        final Gson gson = gsonFactory.apply(jsonElement);
        return gson.fromJson(jsonElement, type);
    }

}

Demo

GsonBuilder factory, GsonBuilder , Gson . gsonFactoryJsonDeserializer : B -. (a.key a.b.key), , .

private static GsonBuilder createDefaultRealGsonBuilder() {
    return new GsonBuilder()
            // build your "real" Gson builder here by registering necessary type adapters, etc...
            ;
}

public static void main(final String... args) {
    final Gson rootGson = createDefaultRealGsonBuilder()
            .registerTypeAdapter(
                    A.class,
                    gsonFactoryJsonDeserializer(
                            jsonElement -> jsonElement.getAsJsonObject()
                                    .get("key")
                                    .getAsJsonPrimitive()
                                    .getAsInt()
                            ,
                            key -> createDefaultRealGsonBuilder()
                                    .registerTypeAdapter(B.class, (InstanceCreator<B>) type -> new B(key))
                                    .create()
                    )
            )
            .create();
    final A a = rootGson.fromJson("{\"key\":2048,\"b\":{\"x\":20,\"y\":30}}", A.class);
    if ( a.key != a.b.key ) {
        throw new AssertionError("NOT working!");
    }
    out.println("Working.");
}

:

.

, , , , Gson - , .

+1

Source: https://habr.com/ru/post/1665782/


All Articles