Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MismatchedInputException: No Object Id found for an instance of #378

Closed
veniaminM opened this issue Sep 30, 2020 · 10 comments
Closed

MismatchedInputException: No Object Id found for an instance of #378

veniaminM opened this issue Sep 30, 2020 · 10 comments
Labels
2.11 Issue affecting version 2.11 bug

Comments

@veniaminM
Copy link

veniaminM commented Sep 30, 2020

Bug description
When I pass no value for an ID field with default value I get an exception:
"com.fasterxml.jackson.databind.exc.MismatchedInputException: No Object Id found for an instance of"
whereas com.fasterxml.jackson.databind.ObjectMapper sets default value.

To Reproduce

package testSuite

import com.fasterxml.jackson.annotation.JsonIdentityInfo
import com.fasterxml.jackson.annotation.ObjectIdGenerators
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.junit.Test

class ObjectId {

  @JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class)
  class MyStateObject(
    var id: String = "0",
    var name: String? = null,
    var description: String? = null
  )

  @Test
  fun readJson() {
    val mapperDataBind = ObjectMapper()
    val stateDataBind1 = mapperDataBind.readValue("{\"id\":\"1\",\"name\":\"abc\"}", MyStateObject::class.java)
    assert(stateDataBind1.id == "1")
    val stateDataBind2 = mapperDataBind.readValue("{\"name\":\"abc\"}", MyStateObject::class.java)
    assert(stateDataBind2.id == "0")


    val mapperModule = jacksonObjectMapper()
    val stateModule1 = mapperModule.readValue("{\"id\":\"1\",\"name\":\"abc\"}", MyStateObject::class.java)
    assert(stateModule1.id == "1")
    val stateModule2 = mapperModule.readValue("{\"name\":\"abc\"}", MyStateObject::class.java) // here I get an exception
    assert(stateModule2.id == "0")
  }
}

Expected behavior

  • Not to get an exception.
  • Default value has set.

Versions
Kotlin: 1.3.72
Jackson-module-kotlin: 2.9.9
Jackson-databind: 2.9.9

@veniaminM veniaminM added the bug label Sep 30, 2020
@cowtowncoder
Copy link
Member

I don't think this is Kotlin-specific, but I also do not thing this is a bug -- if you specify that a type is to have Object Id, it better have it. If not, that is an error condition. That is the intended behavior.

There is an existing setting:

DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS

that you can disable which does change some of the requirements, but not sure it prevents this particular error (more meant to handle invalid Object Id references).

I also wonder if you actually need @JsonIdentityInfo here: it is meant for resolving cyclic references, and not as generic id generation (although I can see why that might be useful for some cases).
You could probably create simple custom serializer if id just needs to be generated, not really used for resolving graph dependencies.

Finally, it may be worth filing a separate RFE for DeserializationFeature to allow for missing object id, similar to DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY, if you still think it is useful to allow optionality in such cases.

@veniaminM
Copy link
Author

For me it looks like a bug since it's an add-on to a data-binding functionality for Jackson, but it change an original behaviour.

@veniaminM veniaminM reopened this Oct 2, 2020
@dinomite dinomite added the 2.11 Issue affecting version 2.11 label Oct 20, 2020
@k163377
Copy link
Contributor

k163377 commented Mar 19, 2023

I did a little research and it looks like a bug (or inconsistency?) with databind, not jackson-module-kotlin.

First, the deserialization in KotlinValueInstantiator is successful, it is the subsequent validation process that is causing the error.
The direct cause of this validation failure is that the call to readIdProperty is made only for properties that exist on the input JSON.

@cowtowncoder
I can think of a couple of possible fixes, could you please confirm?

@cowtowncoder
Copy link
Member

@k163377 What would the possible fixes be?

@k163377
Copy link
Contributor

k163377 commented Mar 21, 2023

@cowtowncoder
I think there are three possible fixes.

The first is to drop this validation.
The setter-based deserialization does not appear to validate by handleIdValue.

The second is to use the property name specified in the JsonIdentityInfo for validation, rather than looking at the input.
This way, the behavior that looks like a false positive will be eliminated.

Third, even for setter-based deserialization, such input should be an error.
This way, the behavior can be aligned with deserialization using the instantiator.

I am not familiar with the databind codebase, so I apologize if there are any misconceptions.

@cowtowncoder
Copy link
Member

cowtowncoder commented Mar 21, 2023

@k163377 Ok I am not sure I understand the problem wrt property name (maybe you meant value instead?)
I am also not sure there is actual difference with setter-based deserialization: method may be different but missing Object Id should be reported in that case as well.

I also still do not see why missing of an Object Id would be considered a false positive: input does not have the Object Id. Object Id cannot come from defaulting (it must be unique); Jackson's Object Id handling does require Object Id to come from input on deserialization -- this is not optional, or configurable (yet).
While Kotlin may set property that matches object id to some value, this is not something Jackson considers acceptable -- it's just an arbitrary object property. This is different from serialization, during which property can have (unique) Object Id to serialize. But during deserialization this is not the case.
Put another way: value defaulting is not something databind considers at all. Whatever value a property might have after constructed is irrelevant.

But if there was to be an option to disable failure that'd be acceptable; someone would have to add it, implement.
It would be new functionality. I am not quite sure what kind of usage that'd be good for but I assume it makes sense for some case... although value in use does not really sound like Object Id in the sense @JsonIdentityInfo was designed.

@k163377
Copy link
Contributor

k163377 commented Mar 21, 2023

@cowtowncoder

I am also not sure there is actual difference with setter-based deserialization: method may be different but missing Object Id should be reported in that case as well.

The following Java code is not an error in 2.15.0-rc1.

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;

public class ObjectId {
    @JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator.class)
    static class MyStateObject {
        private String id;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }
    }

    @Test
    void test() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        MyStateObject stateDataBind2 = mapper.readValue("{}", MyStateObject.class);
        System.out.println(stateDataBind2.id); // -> null
    }
}

Jackson's Object Id handling does require Object Id to come from input on deserialization -- this is not optional, or configurable (yet).

If this is the case, I think it is necessary to perform validation when deserializing via setter as well.

@cowtowncoder
Copy link
Member

That does sound like a bug @k163377 ... or I am misremembering rules. I agree that the handling via setter/field/constructors should NOT vary.

Could you file a bug against jackson-databind for that specific case?

@k163377
Copy link
Contributor

k163377 commented Mar 22, 2023

@cowtowncoder
Issue submitted.
FasterXML/jackson-databind#3838

@k163377
Copy link
Contributor

k163377 commented Mar 22, 2023

This issue is closed because it is not kotlin-module specific.

@k163377 k163377 closed this as completed Mar 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2.11 Issue affecting version 2.11 bug
Projects
None yet
Development

No branches or pull requests

4 participants