Reputation: 16067
Consider this simple Json:
{
"test": [
0,
3
]
}
Now I want to deserialize it in a simple int array so for that I use a custom deserializer:
class ArrayDeserializer implements JsonDeserializer<int[]> {
@Override
public int[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return context.deserialize(json.getAsJsonObject().getAsJsonArray("test"), int[].class);
}
}
and then:
Gson gson = new GsonBuilder().registerTypeAdapter(int[].class, new ArrayDeserializer()).create();
int[] arr = gson.fromJson(json, int[].class);
which throws:
Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Not a JSON Object: [0,3]
However when I do this:
class ArrayDeserializer implements JsonDeserializer<int[]> {
private static final Gson gson = new Gson();
@Override
public int[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return gson.fromJson(json.getAsJsonObject().getAsJsonArray("test"), int[].class);
}
}
it works and I get the expected output. Why?
Upvotes: 4
Views: 5286
Reputation: 280112
Let's rewrite your ArrayDeserializer
in to an equivalent form but more expressive
class ArrayDeserializer implements JsonDeserializer<int[]> {
@Override
public int[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonArray jsonArray = json.getAsJsonObject().getAsJsonArray("test");
return context.deserialize(jsonArray, int[].class);
}
}
The JsonDeserializationContext#deserialize
javadoc states
Invokes default deserialization on the specified object. It should never be invoked on the element received as a parameter of the
JsonDeserializer.deserialize(JsonElement, Type, JsonDeserializationContext)
method. Doing so will result in an infinite loop sinceGson
will in-turn call the custom deserializer again.
So, even if it had worked, you'd more than likely have a stackoverflow on your hands.
So why didn't it work?
You called (in pseudo-code)
deserialize {test:[0,3]} as int[]
// --> Gson finds ArrayDeserializer mapped to int[]
take given JSON as an object (OK), extract 'test' as JSON array (OK)
deserialize [0,3] as int[]
// --> Gson finds ArrayDeserializer mapped to int[]
take given JSON as an object (FAIL)
This last time you recurred, the JSON was already in the form of a JSON array but your ArrayDeserializer
was expecting a JSON object.
In your second attempt
return gson.fromJson(json.getAsJsonObject().getAsJsonArray("test"), int[].class);
you're again extracting the 'test' JSON array and feeding it to a new Gson
instance on which you haven't registered your ArrayDeserializer
. In effect, you're invoking
new Gson().fromJson("[0,3]", int[].class);
which is supported out of the box and will return an int[]
with the two elements 0 and 3, as expected.
There are simpler solutions.
Define a simple POJO type
class Pojo {
private int[] test;
public int[] getTest() {
return test;
}
public void setTest(int[] test) {
this.test = test;
}
}
and deserialize to it
Pojo pojo = new Gson().fromJson(json, Pojo.class);
int[] arr = pojo.getTest();
Or
Gson gson = new Gson();
JsonArray jsonArray = gson.toJsonTree(json).getAsJsonObject().get("test").getAsJsonArray();
int[] arr = gson.fromJson(jsonArray, int[].class);
Upvotes: 5
Reputation: 41
Well, looking at your desired version of ArrayDeserializer class:
class ArrayDeserializer implements JsonDeserializer<int[]> {
@Override
public int[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return context.deserialize(json.getAsJsonObject().getAsJsonArray("test"), int[].class);
}
}
Inside deserialize
method you are calling again deserialize
, so at the second call, you'll not have an array... that is the reason why you have an IllegalStateException
.
Your method should create an int[]
array (with the appropriate convertions) and then return it. That's why your second version, with gson.fromJson
works, because fromJson
deserializes to int[]
. Maybe you keep using this to do the converting dirt work.
Upvotes: 0