user2336315
user2336315

Reputation: 16067

GSON - Deserialize primitive array

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

Answers (2)

Sotirios Delimanolis
Sotirios Delimanolis

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 since Gson 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

Roque Possamai
Roque Possamai

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

Related Questions