0

I have a json which is something like following:


    [{"type":"chair", "color":"red", "owners":["A","B"]},
    {"type":"vase", "shape":"oval", "owners":["B", "C"]}]

As seen, both entries are different. So I need to deserialize this json and instantiate 2 objects Chair and Vase. The owners above should be List of enum in the appropriate object. The json can have many other different entries like that and in any order. So I am doing this:

List<Map<String, Object>> things = objectMapper.readValue(json,
objectMapper.getTypeFactory().constructCollectionType(List.class, Map.class));

things is a list of maps and each map will correspond to one thing. I iterate over the list and from each map check 'type' and instantiate the appropriate object. Eg:

if map.get("type") == "chair" then new Chair(map.get("color"), map.get("owners"));

Now I have an enum OWNERS. So I do this:

List<OWNERS> owners = (ArrayList<OWNERS>) map.get("owners");

I do get the list properly. But when I verify like this:

assertEquals(OWNERS.A, owners.get(0));

I get error: 'expected java.lang.OWNERS("A") but got java.lang.String("A")'

As you can see type of owners is List. What am I missing? Am I parsing the wrong way?

user2612586
  • 45
  • 1
  • 5
  • Which JSON API are you using? Check the class with the method constructCollectionType if there's a way to map strings to enums. Enum is a java concept, not JSON. – Lluis Martinez Oct 11 '14 at 00:58

3 Answers3

0

Try this

assertEquals(OWNERS.A.toString(), owners.get(0));

Also, this line

List<OWNERS> owners = (ArrayList<OWNERS>) map.get("owners");

at runtime works because of type erasure. It will crash if you try to read an element of the list and cast it to OWNERS (because it will be a list of strings in fact).

Check Java generics - type erasure - when and what happens

Community
  • 1
  • 1
Lluis Martinez
  • 1,868
  • 7
  • 27
  • 40
0

The problem comes from the Map<String, Object>. Your object mapper can't infer the type of the owners, it takes a guess and assumes it's a String.

One thing you could do is convert the string list to a list of owners.

A better solution would be to use something like jackson-databind and annotate your Chair and Vase classes. Jackson has annotations that let it infer the type of an object from a JSON field (like your type field).

Your code should look something like this:

@JsonTypeName("chair")
@JsonTypeInfo(use=Id.NAME, include=As.PROPERTY, property="type")
public class Chair {
    @JsonCreator
    public Chair(@JsonProperty("color") String color, 
                 @JsonProperty("owners") List<OWNERS> owners) { }
}

The JsonTypeInfo annotations tell jackson to use a name of your choice in the type JSON property to indentify your class. The JsonTypeName annotation specifies the name of your class.

If you don't want to bother with annotations you can use jackson's tree model. To load your json content through a Map like interface.

Boris Bera
  • 728
  • 5
  • 17
0

Using Jackson annotations was not an option for me. So I worked with the default that it considers i.e String. I did this:

List owners = (ArrayList) map.get("owners");

And now converted this to my enum by taking the corresponding enum valueOf that String:

OWNERS.valueOf(owners.get(0));

user2612586
  • 45
  • 1
  • 5