I need to deserialize JsonArray to a boolean value. If an array exists and is not empty the value should be set to "true". The problem is, my custom deserializer, while functional, breaks deserialization of the rest of the fields - they are being set to null.
Object:
private static class TestObject {
private String name;
@JsonProperty("arr")
@JsonDeserialize(using = Deserializer.class)
private Boolean exists = null;
private Integer logins;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getExists() {
return exists;
}
public void setExists(boolean exists) {
this.exists = exists;
}
public Integer getLogins() {
return logins;
}
public void setLogins(Integer logins) {
this.logins = logins;
}
@Override
public String toString() {
return "TestObject{" +
"name='" + name + '\'' +
", exists=" + exists +
", logins=" + logins +
'}';
}
}
Deserializer:
public class Deserializer extends JsonDeserializer<Boolean> {
@Override
public Boolean deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException {
if (jp.getCurrentToken() == JsonToken.START_ARRAY) {
return true;
}
return false;
}
}
Test
@Test
public void test() throws JsonParseException, IOException {
Boolean result = deserialize();
}
private Boolean deserialize() throws IOException, JsonParseException,
JsonProcessingException {
TestObject testObject = mapper.readValue("{\n" +
" \"arr\": [\n" +
" {\"value\": \"New\"}\n" +
" ],\n" +
" \"name\": \"name\",\n" +
" \"logins\": 36" +
"}",
TestObject.class);
System.out.println(testObject.toString());
return testObject.getExists();
}
If i remove the "arr" array or move it to the bottom of the Json, everything's fine. If i leave it at the top - TestObject{name='null', exists=true, logins=null}
.
There was a similar question (Jackson Custom Deserializer breaks default ones), but unfortunately it has zero answers. Since the code works properly when i rearrange Json, it doesn't look like custom deserializer is used for all fields, rather Jackson stops deserialization when it executes custom deserializer.
Your deserializer may not be interested in the contents of the array but must still advance the read mark on the parser.
You could read the value of arr
at once and decide according to the size of the collection:
@Override
public Boolean deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException {
JsonNode node = jp.getCodec().readTree(jp);
return node.size() != 0;
}
Deciding on the size of the collection and not on the existence of a collection at all is necessary because either your stringified object contains an arr or the Deserializer.deserialize()
is never executed. The property exist
will be null
in this case. So the only possible semantics to express does not exist is an empty collection.
Just for curiousity I tried a second more explicit way to keep the parser on track. For real world use the above version is definitely preferable.
@Override
public Boolean deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException {
if (jp.currentToken() == JsonToken.START_ARRAY) {
jp.nextToken();
int recursionLevel = 1;
while(recursionLevel > 0) {
switch (jp.currentToken()) {
case START_ARRAY:
// just in case of nested arrays
recursionLevel++;
break;
case END_ARRAY:
recursionLevel--;
break;
}
jp.nextToken();
}
return true;
}
return false;
}
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