Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gson - JsonSerializer in TypeAdapterFactory or generic type in TypeAdapter

Tags:

java

gson

javafx

I am trying to write a TypeAdapterFactory for the JavaFX Types that I am using (different properties and observables). For the ObjectProperty i could write a JsonSerializer/Deserializer making use of a cast to ParamterizedType and then getting the ActualTypeArguments.

This is the code I use to achieve this:

public class ObjectPropertySerializer implements JsonSerializer<ObjectProperty>, JsonDeserializer<ObjectProperty> {

    @Override
    public ObjectProperty deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        Type objectType = ((ParameterizedType)typeOfT).getActualTypeArguments()[0];
        return new SimpleObjectProperty(new GsonBuilder().create().fromJson(json, objectType));
    }

    @Override
    public JsonElement serialize(ObjectProperty src, Type typeOfSrc, JsonSerializationContext context) {
        Type objectType = ((ParameterizedType)typeOfSrc).getActualTypeArguments()[0];
        return new GsonBuilder().create().toJsonTree(src.getValue(), objectType);
    }
}

However I also need to use a TypeAdapterFactory because of nested classes/object and direct serialisation, that do not work when registering the serialisers directly to a GsonBuilder using .registerTypeAdapter().

This is the code of my factory so far (the other properties already work):

public class JFXGsonAdapterFactory implements TypeAdapterFactory {

    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        if ( BooleanProperty.class.isAssignableFrom(type.getRawType()) ) {
            return (TypeAdapter<T>) new BooleanPropertyAdapter();
        }
        else if ( DoubleProperty.class.isAssignableFrom(type.getRawType()) ) {
            return (TypeAdapter<T>) new DoublePropertyAdapter();
        }
        else if (IntegerProperty.class.isAssignableFrom(type.getRawType()) ) {
            return (TypeAdapter<T>) new IntegerPropertyAdapter();
        }
        else if (ObjectProperty.class.isAssignableFrom(type.getRawType()) ) {
            return (TypeAdapter<T>) new ObjectPropertyAdapter(gson);
        }
        else if ( StringProperty.class.isAssignableFrom(type.getRawType()) ) {
            return (TypeAdapter<T>) new StringPropertyAdapter();
        }
        else {
            return null;
        }
    }
}

I can't return an implementation of the serializer/deserializer interfaces here, but need to use an adapter class that extends TypeAdapter.

Here is my TypeAdapter implementation for ObjectProperty:

public class ObjectPropertyAdapter<T> extends TypeAdapter<ObjectProperty> {

    private Gson gson;
    TypeAdapter<JsonElement> elementAdapter;

    public ObjectPropertyAdapter( Gson gson ) {
        this.gson = gson;
        elementAdapter = gson.getAdapter(JsonElement.class);
    }

    @Override
    public void write(JsonWriter out, ObjectProperty value) throws IOException {
        if ( value == null ) {
            out.nullValue();
        } else {
            JsonElement jsonElement = gson.toJsonTree(value.get());
            elementAdapter.write(out, jsonElement);
        }
    }

    @Override
    public ObjectProperty read(JsonReader in) throws IOException {
        if (in.peek() == JsonToken.NULL) {
            in.nextNull();
            return null;
        } else {
            // Read
            return null; // tmp
        }
    }

}

However I am not sure how to get the type of the generic for ObjectProperty in the TypeAdapter and I do not know how to get it from the TypeToken either (since I could pass it to the adapter from the factory).


Summary of the question:

1) How do I cast an implementation of JsonSerializer/Deserializer to TypeAdapter

or

2) How can I get the generic type for e.g. ObjectProperty from the TypeToken?

or

3) Do I need to do something completely different to achieve this?

like image 909
JohnRW Avatar asked Oct 19 '25 16:10

JohnRW


1 Answers

1) You can't. JsonSerializer and JsonDeserializer are the old converter way. New applications should use TypeAdapter, which has a superset of funcionality.

2) Assuming you mean JavaFX's ObjectProperty<T> and you want to extract T's class.

You can access the Type inside TypeToken easily with TypeToken#getType().

I will give the example with List<String> which is the same idea but easily testable.

TypeToken<List<String>> typeToken = new TypeToken<List<String>>(){};

System.out.println("typeToken.getRawType() = " + typeToken.getRawType());
Type type = typeToken.getType();
System.out.println("typeToken.getType() = " + type);
if (type instanceof ParameterizedType) {
  Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
  System.out.println("typeArguments = " + Arrays.toString(typeArguments));
}

Output result:

typeToken.getRawType() = interface java.util.List
typeToken.getType() = java.util.List<java.lang.String>
typeArguments = [class java.lang.String]

3) I think you are nearly there. ;-) Ah, with the TypeAdapter you don't think in terms of JsonObject or JsonElement (DOM style) but you build the object or the JSON representation on the fly (stream style).

Also, in your question you say

However I also need to use a TypeAdapterFactory because of nested classes/object and direct serialisation, that do not work when registering the serialisers directly to a GsonBuilder using .registerTypeAdapter().

Maybe you need to use GsonBuilder#registerTypeHierarchyAdapter() and look for those nested classes. It will depend on your problem details.

like image 142
sargue Avatar answered Oct 21 '25 07:10

sargue