简体   繁体   中英

converting json to java object giving wrong type of enum

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?

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

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.

Using Jackson annotations was not an option for me. So I worked with the default that it considers ie 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));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM