When I got the JSON with Redmine REST API, the following notation appeared in "custom_fields".
"custom_fields": [
{
"id": 1,
"name": "Item 1",
"value": "a"
},
{
"id": 2,
"name": "Item 2",
"multiple": true,
"value": [
"b"
]
},
{
"id": 3,
"name": "Item 3",
"multiple": true,
"value": [
"c",
"d"
]
},
It seems that if the content of " value "
is " multiple ": true
, it is an array object, otherwise it is single.
As in the above example, there are 3 patterns.
There is also a method to convert all to an array even if it is single at the time of deserialization (JSON → Java), but since it can not be restored to the original expression at the time of serialization (I do not know whether it is a single or an array with 1 element), put that control With that in mind, I also thought about a general-purpose implementation.
The object to put data at the time of deserialization is as follows.
public class MultipleType<T> {
private T value;
private List<T> values;
public boolean isMultiple() {
return values != null;
}
//Getter below/setter
}
Use either value or values and make the other null.
The above "custom_fields" looks like the following.
public class CustomField {
private String id;
private String name;
private boolean multiple;
private MultipleType<String> value;
//Getter below/setter
}
I want to use the functions of GSON as much as possible, so I will use the same field name as JSON.
Implement GSON's custom serializer / deserializer as follows.
public class MultipleTypeAdapter implements JsonDeserializer<MultipleType<?>>, JsonSerializer<MultipleType<?>> {
@Override
public MultipleType<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
final MultipleType<?> result = new MultipleType<>();
if (json.isJsonArray()) {
result.setValues(deserializeArray(json, typeOfT, context));
} else {
result.setValue(context.deserialize(json, getGenericType(typeOfT)));
}
return result;
}
private <T> List<T> deserializeArray(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
final List<T> values = new ArrayList<>();
final Type t = getGenericType(typeOfT);
for (JsonElement e : json.getAsJsonArray()) {
values.add(context.deserialize(e, t));
}
return values;
}
/* get actual Type of <?> */
private Type getGenericType(Type typeOfT) {
return ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
}
@Override
public JsonElement serialize(MultipleType<?> src, Type typeOfSrc, JsonSerializationContext context) {
return context.serialize(src.isMultiple() ? src.getValues() : src.getValue());
}
}
There are three main points.
MultipleType
Especially 2 is a little complicated.
First, we need to know what the argument Type typeOfT
of thedeserialize
method represents.
It contains an object that represents the type parameters of the JsonDeserializer
interface.
(To be exact, the type of object actually used at runtime)
Generics type parameters are lost at compile time, so you can refer to them at run time.
The definition tells us that typeOfT
isMultipleType <?>
, But since we need to know the<?>
Part, we are getting more internal type parameters with the getGenericType
method. I will.
The reason why the runtime type of the type parameter <?>
Is necessary is that if you give the correct type with the context.deserialize
method, the deserialization after that can be left to the function of GSON. ..
In particular, you don't have to handwrite complex structure deserialization where the fields of the data object are of non-primitive type (including String).
The usage of GSON is as follows.
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MultipleType.class, new MultipleTypeAdapter());
final Gson gson = gsonBuilder.create();
Since the MultipleTypeAdapter
class is not a generic type, it is a miso that there is no warning.
This is why we used <?>
(Wildcards) for the custom deserializer / serializer type parameters.
(I can't explain why wildcards are OK ...)
I use the generic type of Java as "generics" in sentences and the pronunciation as "generics". Isn't it difficult to say "generics"?
Recommended Posts