FasterXML / jackson-databind

General data-binding package for Jackson (2.x): works on streaming API (core) implementation(s)
Apache License 2.0
3.51k stars 1.37k forks source link

Allow coercion of '[ ]' into empty/null String #2124

Open ulfandersson opened 6 years ago

ulfandersson commented 6 years ago

I'm using an API which uses [] as null, therefore I'm using DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT.

Sometimes arrays can contain null as well, giving a structure like this example:

{
    "test" : [
        "hello",
        []
    ]
}

Jackson fails to parse this when trying to parse it to a List\<String> or String[].

Stack trace for the List\<String> case:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token
 at [Source: (String)"{"test":["hello",[]]}"; line: 1, column: 18] (through reference chain: AcceptEmptyArrayAsNullObject$StringListTest["test"]->java.util.ArrayList[1])

    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1342)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1138)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1092)
    at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseString(StdDeserializer.java:569)
    at com.fasterxml.jackson.databind.deser.std.StringCollectionDeserializer.deserialize(StringCollectionDeserializer.java:203)
    at com.fasterxml.jackson.databind.deser.std.StringCollectionDeserializer.deserialize(StringCollectionDeserializer.java:169)
    at com.fasterxml.jackson.databind.deser.std.StringCollectionDeserializer.deserialize(StringCollectionDeserializer.java:21)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2992)

Stack trace for the String[] case:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token
 at [Source: (String)"{"test":["hello",[]]}"; line: 1, column: 18] (through reference chain: AcceptEmptyArrayAsNullObject$StringArrayTest["test"]->java.lang.Object[][1])

    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1342)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1138)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1092)
    at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseString(StdDeserializer.java:569)
    at com.fasterxml.jackson.databind.deser.std.StringArrayDeserializer.deserialize(StringArrayDeserializer.java:157)
    at com.fasterxml.jackson.databind.deser.std.StringArrayDeserializer.deserialize(StringArrayDeserializer.java:21)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2992)

However, the following works when parsed to List\<Integer> or Integer[]:

{
    "test" : [
        1,
        []
    ]
}

Jackson version used: 2.9.4.

Test case for the above: JacksonStringArrayTest.zip

cowtowncoder commented 6 years ago

This may be down to naming problem -- it should probably have been named ACCEPT_EMPTY_ARRAY_AS_NULL_POJO, not _OBJECT -- but empty Array JSON will only be considered equivalent to null for POJOs. And not for scalar types such as Strings.

So, at this point this is a limitation of implementation, and there are no plans for changing this particular limitation.

I would be interested in figuring out a way forward, and part of this would be understanding why and how is empty array being used as marker. I was under impression that some languages/platforms (PHP?) used it as a marker for structured values, but did not think it would be used in place of scalar values like numbers, booleans or Strings.

ulfandersson commented 5 years ago

Thank you for your answer! What confused me is that it is supported for Integer objects (but perhaps this is only because the handling is similar as for POJOs)? The only case I am really interested in (except for POJOs) is for strings.

The source of the data is a couchbase database document. It's probably due to a null/empty string is somewhere converted to an empty array when storing the document.

Maybe ACCEPT_EMPTY_ARRAY_AS_NULL_STRING could be added? :)