question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

JsonDeserializer throws runtime error when parameterized type is not of type collection and JsonElement is of type JsonArray

See original GitHub issue

When jsonElement is of type JsonArray and the JsonDeserialization<T> type is not a collection, Gson will always throw an IllegalStateException.

image

Above is the full code, note that Example is an empty class. Below is an image of the direct deserialization in debug mode. Note that the contents of instrumentJson are irrelevant and that it is a correctly streamed JsonObject from a JsonArray.

image

However, if I decide to serialize to most-any other Objects, the jsonDeserializerContext#deserialize has no problem whatsoever.

deserialize object random stream support

So my educated guess is if the current type is not matching the JsonElement subtype (JsonObject or JsonArray), then it causes problems.

Below is confirmation that jsonElement is of type JsonArray

image

My problem is sovled if I change the return type to List<Example> as seen below.

problem solved

But this does not fix my use-case of only wanting to return the first element from this list. It doesn’t fit my design pattern either.

Context

The original issue is the designers of the API I am deserializing from, thought it would be cool for a singular resource endpoint to instead of return a JsonObject, return a JsonArray that would always be of size 1. It is confirmed that this array will always be size one but they have no plans to change the API and I would prefer to keep the list#get(0) logic out of my controller if possible and return the single element from the deserializing class.

Major Inquiry

It is possible to deserialize a JsonArray without having to return a collection type when implementing JsonDeserializer<T>?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5

github_iconTop GitHub Comments

1reaction
bitforcecommented, Feb 5, 2021

Thank you for the deep insight; apologies, I did not include the version I was using (2.8.0). Consider this closed.

0reactions
Marcono1234commented, Feb 5, 2021

Thanks! The exception message does indeed come from Gson, but it appears you are using Gson 2.8.1 (or older). It might be worth upgrading to the latest version (2.8.6).

The problem is that the JsonDeserializer you are registering is calling itself: Initially you are telling Gson to deserialize your Example JSON array as Example.class which works fine but then your JsonDeserializer calls jsonDeserializationContext.deserialize(instrumentJson, Example.class) which invokes itself again (since it is registered for Example.class), this time with a single Example JSON object which therefore causes the exception you are seeing.

If you already know that your data is an array of Example and you only want the first one, then it might be easiest to directly access the JsonArray element or to use a JsonReader (depending on the form in which you receive the JSON data):

public static void main(String[] args) throws IOException {
    JsonArray array = new JsonArray();
    JsonObject object = new JsonObject();
    object.addProperty("name", "bitforce");
    array.add(object);
    
    System.out.println(fromJsonArray(array));
    
    try (Reader jsonString = new StringReader("[{\"name\":\"bitforce\"}]")) {
        System.out.println(fromJsonString(jsonString));
    }
}

private static Example fromJsonArray(JsonArray jsonArray) {
    if (jsonArray.size() > 0) {
        return new Gson().fromJson(jsonArray.get(0), Example.class);
    } else {
        throw new IllegalArgumentException("...");
    }
}

private static Example fromJsonString(Reader jsonString) throws IOException {
    JsonReader jsonReader = new JsonReader(jsonString);
    jsonReader.beginArray();
    if (jsonReader.hasNext()) {
        return new Gson().fromJson(jsonReader, Example.class);
    }
    throw new IllegalArgumentException("...");
}

Otherwise if the Example array appears nested within other model classes, it becomes slightly more complicated. How this can be solved depends on the actual model classes you are using. You could implement a custom TypeAdapterFactory which creates an adapter which begins reading the array and then calls a delegate for deserializing the Example object. If you always wan’t JSON arrays of Example to be deserialized as single Example, then you could register that factory on the GsonBuilder. Otherwise you could use the @JsonAdapter annotation on fields of type Example and then specify the TypeAdapterFactory for that annotation (though don’t use Gson.getDelegateAdapter(...) in the TypeAdapterFactory then because it is currently broken for @JsonAdapter, see #1028).

Read more comments on GitHub >

github_iconTop Results From Across the Web

Deserialize a List<T> object with Gson? - Stack Overflow
Method to deserialize generic collection: import java.lang.reflect.Type; import com.google.gson.reflect.TypeToken; ... Type listType = new ...
Read more >
Serializing and Deserializing a List with Gson - Baeldung
Instead, while deserializing, the Collection must be of a specific, generic type. The correct way to deserialize the List would be:
Read more >
Gson User Guide - Google Sites
Serializing and Deserializing Collection with Objects of Arbitrary Types. Sometimes you are dealing with JSON array that contains mixed types. For example: [' ......
Read more >
How to serialize properties of derived classes with System ...
In this article. Serialize properties of derived classes; Polymorphic type discriminators; Configure polymorphism with the contract model ...
Read more >
How to Deserialize JSON Into Dynamic Object in C# - Code ...
So, we're not getting dynamic support in the native JSON library in ... Because, under the hood, this is a boxed JsonElement ,...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found