Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson custom deserializer breaks deserialization of other fields

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.

like image 861
Dmitry Korchemkin Avatar asked Oct 19 '25 14:10

Dmitry Korchemkin


1 Answers

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;
}
like image 83
blafasel Avatar answered Oct 21 '25 03:10

blafasel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!