I have a class that needs to be deserialized from JSON using Jackson. The class structure looks like this:
public class A {
public B b;
}
public class B {
public List<C> c;
}
public class C {
public String s;
public Long l1;
public Long l2;
public Long l3;
}
Deserializing objects mostly works fine; except, it is inter-operating with buggy code that emitted the wrong value when the list was empty. That is, instead of emitting:
{ "b" : { "c" : [] } }
it emitted:
{ "b" : { "c" : {} } }
Jackson throws this exception when it encounters this:
org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
at [Source: [B@1ad5cabc; line: 1, column: 896] (through reference chain: A["b"]->B["c"])
This all makes sense, of course. The input is wrong.
However, the empty list in this case isn't relevant to any of the code; if it was null
I wouldn't care. Is there any way to tell Jackson to ignore this property (and store null
) if it's not possible to deserialize it?
I've tried using a custom deserializer:
public class CListDeserializer extends JsonDeserializer<List<C>>
{
public CListDeserializer() { }
@Override
public List<C> deserialize(JsonParser arg0,
DeserializationContext arg1) throws IOException,
JsonProcessingException
{
try
{
return arg0.readValueAs(new TypeReference<List<C>>(){});
}
catch (JsonMappingException jme)
{
}
return null;
}
}
And if I add the annotation to the field:
@JsonDeserialize(using=CListDeserializer.class)
public List<C> c;
I get this exception instead:
org.codehaus.jackson.map.JsonMappingException: Can not instantiate value of type [simple type, class asgard.ChangeEntry] from JSON String; no single-String constructor/factory method (through reference chain: A["b"])
Note that this exception only happens if I try deserializing the outer type A
-- if I pull out the inner serialized value for B
, and deserialize that, it works fine.
I've figured out why the custom deserializer doesn't work: the readValueAs
method consumes the {
, which triggers the exception, leaving }
as the next token. This closes the inner object, and the parser will then think the enum value encountered next needs to be parsed as the inner type, not as the enum type.
I'll try this tomorrow, but I think the way to go is this, in the deserialize
method:
ObjectMapper om = ...;
JsonNode node = arg0.readValueAs(JsonNode.class);
try
{
return om.readValue(node, new TypeReference<List<C>>(){});
}
catch (JsonMappingException jme)
{
}
return null;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With