Skip to content

IonValueDeserializer does not handle getNullValue correctly for a missing property #317

@atokuzAmzn

Description

@atokuzAmzn

Hello,

Following #295, we switched to 2.13 from 2.12 to be able to handle null.struct deserialization correctly but we bumped into a different problem in 2.13

We were trying to deserialize a test input into an entity with an optional IonValue field. We received IndexOutOfBoundsException when this property was missing in the input.

This is the stacktrace of the error:

at ExampleEntityTest.deserialization test without optional data(ExampleEntityTest.groovy:219)
Caused by: java.lang.IndexOutOfBoundsException: 0
at com.amazon.ion.impl.lite.IonContainerLite.get_child(IonContainerLite.java:663)
at com.amazon.ion.impl.lite.IonContainerLite.get(IonContainerLite.java:151)
at com.fasterxml.jackson.dataformat.ion.IonParser.getIonValue(IonParser.java:424)
at com.fasterxml.jackson.dataformat.ion.IonParser.getEmbeddedObject(IonParser.java:442)
at com.fasterxml.jackson.dataformat.ion.ionvalue.IonValueDeserializer.getNullValue(IonValueDeserializer.java:61)
at com.fasterxml.jackson.dataformat.ion.ionvalue.IonValueDeserializer.getNullValue(IonValueDeserializer.java:32)
at com.fasterxml.jackson.databind.JsonDeserializer.getAbsentValue(JsonDeserializer.java:350)
at com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer._findMissing(PropertyValueBuffer.java:203)
at com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer.getParameters(PropertyValueBuffer.java:158)
at com.fasterxml.jackson.databind.deser.ValueInstantiator.createFromObjectWith(ValueInstantiator.java:288)
at com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:202)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:518)

The problem occurs when IonValueDeserializer tries to execute the overridden getNullValue for the missing property. In the beginning of this method it tries to get the embedded object from IonParser.

    @Override
    public IonValue getNullValue(DeserializationContext ctxt) throws JsonMappingException {
        try {
            Object embeddedObj = ctxt.getParser().getEmbeddedObject();
    ...

    }

IonParser's getEmbeddedObject method basically checks the token first but since in our case _currToken is END_OBJECT for the missing property so it skips the whole method and fallback to getIonValue().

Now in getIonValue _currToken is assigned to JsonToken.VALUE_EMBEDDED_OBJECT directly and it tries to write reader into an IonList. But since there is nothing to read for a missing property no IonValue is written into IonList.

And since there is no size check, in the next line when it tries to access the first value in the list we get an java.lang.IndexOutOfBoundsException

    @SuppressWarnings("resource")
    private IonValue getIonValue() throws IOException {
        if (_system == null) {
            throw new IllegalStateException("This "+getClass().getSimpleName()+" instance cannot be used for IonValue mapping");
        }
        _currToken = JsonToken.VALUE_EMBEDDED_OBJECT;
        IonList l = _system.newEmptyList();
        IonWriter writer = _system.newWriter(l);
        writer.writeValue(_reader);
        IonValue v = l.get(0);
        v.removeFromContainer();
        return v;
    }

    @Override
    public Object getEmbeddedObject() throws IOException {
        if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) {
            switch (_reader.getType()) {
            case TIMESTAMP:
                return _reader.timestampValue();
            case BLOB:
            case CLOB:
                return _reader.newBytes();
            // What about CLOB?
            default:
            }
        }
        return getIonValue();
    }

Can someone confirm if this is a bug or not?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions