0

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 !

Loup Ollivier
  • 89
  • 2
  • 7

2 Answers2

1

@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.

Loup Ollivier
  • 89
  • 2
  • 7
0

Your problem is that the name of the attributes in the JSON is different from what you have in the code. Note in your JSON the first letter is upper case.

You have a couple options around that: - Rename them in your JSON - Rename on the code side with @JsonProperty("newname") or using builder.rename(currentName, newName). - Implement a custom PropertyNameResolver that delegates name resolution to ConventionalBeanPropertyNameResolver and then just changes the first letter to upper case.

You can implement custom converters as you started, but this will be quite some work if you need to do it for each type.

I recommend implementing the custom name resolver.

eugen
  • 5,856
  • 2
  • 29
  • 26
  • Note that desired property is nested inside of another object. – Code-Apprentice May 13 '19 at 19:52
  • @eugen thank you for your quick answer, I have no hands on the json I receive. I tried your second solution but it was still loging the same error... I think the fact that json are nested may disturb the deserializer. Also, I didn't found how to try your third solution you recommended to me, but I will look at it again tomorrow. – Loup Ollivier May 13 '19 at 19:54
  • After new tests, nested json from the rest api trully prevent @JsonProperty or builder.rename(...) to work. Unfortunately he PropertyNameResolver and ConventionalBeanPropertyNameResolver was not simple enought for me to use them... The custom converter is actually doing a great job in this case. – Loup Ollivier May 14 '19 at 13:38