0

I have a POJO like this

class POJO {
    Map<Integer, Integer> map;
    //Getter and Setter for map here
}

And I have a json (list of POJO)

[
    {
        "200": 10
    },
    {
        "20": 100,
        "30": 400
    }
]

During Jackson deserialization if I do

String s = ... //s has the above json
List<POJO> pojoList = mapper.readValue(s, new TypeReference<List<POJO>>() {});
System.out.println(pojoList.get(0).getMap().get(20)); //prints 100

then there is no problem

But if I use generic List like

List<POJO> pojoList = mapper.readValue(s, List.class);

then in the System.out.println it throws ClassCastException

java.util.LinkedHashMap cannot be cast to mycode.model.POJO

I understand that if I just tell Jackson List it deserializes each object as Map and not as type POJO. So when I try to access pojoList.get(0).getMap() it throws exception

(Note: Printing pojoList.get(0) gives no problem and prints {"200":10})

My question is why didn't it throw exception during deserialization itself. Did the type of object on LHS ignored?

Thanks..

Thiyagu
  • 17,362
  • 5
  • 42
  • 79

1 Answers1

2

My question is why didn't it throw exception during deserialization itself. Did the type of object on LHS ignored?

You have a misunderstanding of how Java works. The runtime evaluation of these two statements

List<POJO> pojoList = mapper.readValue(s, List.class);
mapper.readValue(s, List.class);

is exactly the same. In other words, an assigned variable plays no role at runtime. At compile time, all it's good for is potentially providing context for generics and poly expressions.

When you invoke your

mapper.readValue(s, List.class);

you're telling Jackson to deserialize the JSON into a List. That's it. You give it no other information. There's absolutely no way for Jackson to guess that you meant a list of POJO or anything else. As such, Jackson guesses and uses its own defaults. For JSON objects, that default is LinkedHashMap.

What's more, List.class evaluates to Class<List> and therefore the return type of your readValue is the raw List type. When working with raw types, generics are erased. You therefore don't get a compilation error, but you should have a warning about unchecked conversions.

You should read


On the other hand, this

List<POJO> pojoList = mapper.readValue(s, new TypeReference<List<POJO>>() {});

works because you are giving Jackson ALL the information it needs to properly deserialize the JSON. It's a List (a JSON array) of POJO elements (JSON objects).

This, however, has its own problems. The return type (ie. compile time) of readValue is inferred from the context (that I mentioned earlier) and bound to List<POJO> since that is the type of the expression you're assigning the result to. The TypeReference provides no type information for the generics here. Because of this, you can do something like

List<String> stringList = mapper.readValue(s, new TypeReference<List<POJO>>() {}); 

and it will compile without error. At runtime though, everything will probably break.

Community
  • 1
  • 1
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724