Skip to content

Deserialize missing value of EXTERNAL_PROPERTY type using custom NullValueProvider #3533

Closed
@fxshlein

Description

@fxshlein

Is your feature request related to a problem? Please describe.
I am currently trying to deserialize a structure with a generic field that has an external type property, where the type property is always present, but the actual property may be absent, in which case it should just use a custom NullValueProvider.

The json can either look like this:

{
  "type": "inner-implementation",
  "value": {
    "foo": "bar"
  }
}

Or like this, in which case value should use the NullValueProvider:

{
  "type": "inner-implementation"
}

The outer type looks like this:

public class Wrapper<I extends Inner> {
    @JsonTypeInfo(use = Id.NAME, include = As.EXTERNAL_PROPERTY)
    private I value;

    public Wrapper(@JsonProperty(value = "value", required = true) I value) {
        this.value = value;
    }
}

And the inner types all implement this interface:

@JsonSubTypes({
        @Type(InnerImplementation.class),
})
public interface Inner {}

With an implementation of the inner type looking like this:

@JsonTypeName("inner-implementation")
@JsonDeserialize(using = InnerImplementationDeserializer.class)
public class InnerImplementation implements Inner {
    @Nullable
    private String foo;

    public InnerImplementation(@Nullable String foo) {
        this.foo = foo;
    }
}

Each implementation of Inner also has a deserializer which implements the getNullValue method:

class InnerImplementationDeserializer extends StdNodeBasedDeserializer<InnerImplementation> {
    protected InnerImplementationDeserializer() {
        super(TypeFactory.defaultInstance().constructType(InnerImplementation.class));
    }

    @Override
    public InnerImplementation convert(JsonNode root, DeserializationContext ctxt) throws IOException {
        JsonNode foo = root.get("foo");

        return new InnerImplementation(foo != null ? foo.textValue() : null);
    }

    @Override
    public InnerImplementation getNullValue(DeserializationContext ctxt) throws JsonMappingException {
        return new InnerImplementation(null);
    }
}

I have also disabled FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY.

The first snippet deserializes without problems, and when I remove required = true from the value property, the second snippet deserializes with Wrapper#value being null. With required = true however, a com.fasterxml.jackson.databind.exc.MismatchedInputException is thrown.

The exception originates from this part of the jackson databind codebase.

Describe the solution you'd like
In the linked snippet of jackson code, it would in my opinion be appropriate to use the custom NullValueProviders if FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY is disabled.

Usage example
The feature would work like shown above, however depending on how reasonable this approach generally is, the option FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY could be disabled by default.

Additional context
The underlying WRAPPER_ARRAY property used in the background by EXTERNAL_PROPERTY was already changed to support this behaviour: #2467

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions