0

I am trying to deserialize a JSON data to a POJO.

The issue is that the list object is coming as a string, and gson gives an IllegalStateExceptioState. How can I parse the string as a list to an ArrayList using gson?

JSON DATA

{
   "report_id":1943,
   "history_id":3302654,
   "project_id":null,
   "owner_emails":"[\"[email protected]\"]",
   "message":"Array\n(\n    [name] => SOMENAME\n    [age] => 36\n    [gender] => male\n)\n"
}

POJO:

    public class EventData {
    
        private static Gson gson = new Gson();
    
        @SerializedName("report_id")
        public String reportID;
    
        @SerializedName("history_id")
        public String historyID;
    
        @SerializedName("project_id")
        public String projectID;
    
        @SerializedName("owner_emails")
        public ArrayList<String> ownerEmails = new ArrayList<String>();
    
        @SerializedName("message")
        public String message;
    
    
        @SerializedName("title")
        public String title;
    
        public CrawlerNotifiedEventData(){
            this.projectID = "Undefined";
            this.reportID = "Undefined";
            this.historyID = "Undefined";
            this.title = "";
        }
    
        public String toJson(boolean base64Encode) throws java.io.UnsupportedEncodingException{
            
            String json = gson.toJson(this, CrawlerNotifiedEventData.class);
            
            if(base64Encode)
                return Base64.getEncoder().encodeToString(json.getBytes("UTF8"));
    
            return json;
        }
    
        public String toJson() throws java.io.UnsupportedEncodingException{
            return this.toJson(false);
        }
    
        public static EventData builder(String json){
            return gson.fromJson(json, EventData.class);   
        }
    }

Deserialization:

EventData eventData = EventData.builder(json);

While deserializing i get the following error

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 252 path $.owner_emails
5
  • are you using any library?? like retrofit? Commented Mar 4, 2021 at 7:00
  • No retrofit, just Gson and java standard librarires. Commented Mar 4, 2021 at 7:04
  • 1
    Do you see that your JSON is misdesigned and holds an array in a string, not self, so that Gson can't map a string to a list of strings? Ask your JSON producer to produce a properly designed response and not wrap the emails field in a string. If it's not possible and you must deal with such a badly designed API, then either deserialize the field value in a later stage, or write a custom JSON-unpacking deserializer to deal with some kind of stuff (hint: use @JsonAdapter for the field). Commented Mar 4, 2021 at 7:21
  • @fluffy can't change the JSON data being produced. Wil look into JsonAdapter. Commented Mar 4, 2021 at 7:26
  • Valid format of array object should be: "message": [{"name": "Rajiv", "age": 36, "gender": "male"}]. Use regular Commented Mar 4, 2021 at 7:31

3 Answers 3

1

Boxing structured data in a string where it is unnecessary is a very common design issue across different serialization approaches. Fortunately, Gson can deal with fields like owner_emails (but not message of course).

Merely create a type adapter factory than can create a type adapter for a particular type by substituting the original one and doing a bit of more work. The adapter is supposed to read the payload as string and delegate the string deserialization to the type adapter it substitutes.

public final class JsonStringBoxTypeAdapterFactory
        implements TypeAdapterFactory {

    private JsonStringBoxTypeAdapterFactory() {
    }

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        final TypeAdapter<T> adapter = gson.getAdapter(typeToken);
        return new TypeAdapter<T>() {
            @Override
            public void write(final JsonWriter out, final T value) {
                throw new UnsupportedOperationException(); // TODO
            }

            @Override
            public T read(final JsonReader in)
                    throws IOException {
                return adapter.fromJson(in.nextString());
            }
        };
    }

}
@AllArgsConstructor
@ToString
@EqualsAndHashCode
final class EventData {

    @SerializedName("owner_emails")
    @JsonAdapter(JsonStringBoxTypeAdapterFactory.class)
    List<String> ownerEmails;

}

The unit test below will be green:

final EventData eventData = gson.fromJson(json, EventData.class);
Assertions.assertEquals(new EventData(ImmutableList.of("[email protected]")), eventData);

That's it.

Sign up to request clarification or add additional context in comments.

Comments

0

"owner_emails" is curently a string as follows

"owner_emails":"[\"[email protected]\"]"

It should be

"owner_emails": ["[email protected]"]

to be considered as array. You can manually remove the quotes and parse it.

Or you can parse it using JsonElement in Gson

Comments

0

You can use ObjectMapper from jackson library for this conversion.

Sample code of conversion::

public <T> T mapResource(Object resource, Class<T> clazz) {
        try {
            return objectMapper.readValue(objectMapper.writeValueAsString(resource), clazz);
        } catch (IOException ex) {
            throw new Exception();
        }
    }

Modify the model for a list like::

 public class Reportdata{
   
   private List<String> owner_emails = new ArrayList(); 

   @JsonDeserialize(contentAs = CustomClass.class)
   private List<CustomClass> customClassList =  new ArrayList();  
   ....// setter and getter
}

In addition to this, while creating the ObjectMapper object you can pass or register the module/ your custom module for deserialization in object like below.

objectMapper.setDefaultPropertyInclusion(Include.NON_EMPTY);
objectMapper.disable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
objectMapper.registerModule(new JavaTimeModule());

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.