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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With