Search code examples
jsonparsingnestedgenson

How can I parse nested json using genson?


I'm getting results from a REST API, it is a list of json object representing users with nested json objects inside each user. My problem is that nested properties names do not correspond to the bean properties I got in my code. But they are so inappropriate that I really want to not keep the API nested properties names...

I am using genson 1.5 with java 8, and lombok for my beans. I tried to use simple deserializer and then a Converter but without success.

Here is an example of what I receive from the API:

[
   {
      "FirstName": "Jack",
      "LastName": "Sparrow",
      "Adress": {
                   "String1": "Toulon",
                   "String2": "France",
                   "String3": "83",
                }
   },
   {
      "FirstName": "I am",
      "LastName": "Groot",
      "Adress": {
                   "String1": "Marseille",
                   "String2": "France",
                   "String3": "13",
                }
   },
]

Here is the bean I want to have as a new format:

@Data
public class User {
   private String firstName;
   private String lastName;
   private String country; //this represents String2 from the API
}

I already tried those solutions:

  • Just using the deserialize recommended (error because not getting the same properties from the api compared to my bean):
private Genson genson = new Genson();

public List<User> getUserList() {
   Response response = api.target(url)...get();
   List<User> users = genson.deserialize(response.readEntity(String.class), new GenericType<List<User>>(){});
   return users;
}
  • Use a converter to get the same parameters as my bean
private Genson genson = new GensonBuilder().withConverters(new UserConverter()).create();

public List<User> getUserList() {
   Response response = api.target(url)...get();
   List<User> users = this.genson.deserialize(response.readEntity(String.class), new genericType<List<User>>(){});
   return users;
}
public class UserConverter implements Converter<User> {

    public User deserialize(ObjectReader reader, Context ctx) throws Exception {
        User user = new User();
        reader.beginObject();

        while (reader.hasNext()) {
            reader.next();
            if ("FirstName".equals(reader.name())) {
                user.setFirstName(reader.valueAsString());
            } else if ("LastName".equals(reader.name())) {
                user.setLastName(reader.valueAsString());
            } else if ("Adress".equals(reader.name())) {
                reader.beginObject();
                while (reader.hasNext()) {
                    if ("String2".equals(reader.name())) {
                        user.setCountry(reader.valueAsString());
                    } else {
                        reader.skipValue();
                    }
                }
                reader.endObject();
            } else {
                reader.skipValue();
            }
        }

        reader.endObject();
        return user;
    }

    @Override
    public void serialize(User object, ObjectWriter writer, Context ctx) throws Exception {
        // TODO Auto-generated method stub

    }
}

The error is:

com.owlike.genson.JsonBindingException: Could not deserialize to type interface java.util.List
    at com.owlike.genson.Genson.deserialize(Genson.java:384)
    ...

Caused by: com.owlike.genson.stream.JsonStreamException: Illegal character at row 0 and column 660 expected } but read '{' !
    at com.owlike.genson.stream.JsonReader.newWrongTokenException(JsonReader.java:942)
    ...

Genson is giving an example at http://genson.io/Documentation/UserGuide/#custom-serde but they read a list of integer as a value this is why I tried a nested while for nested json...

If someone have ideas on how to deal with my problem, thank you very much !


Solution

  • @eugen Thank you for your answer, I tried @JsonProperty above each of my bean parameters but unfortunately it was not working.

    Then a collegue came and we added in the UserConverter:

    private GenericType<Map<String, String>> adressMap = new GenericType<Map<String, String>>() {};
    

    and changed the second while for:

    else if ("Adress".equals(reader.name())) {
       user.string2Value((ctx.genson.deserialize(adressMap, reader, ctx)).get("String2"));
    }
    

    this is actually working.