On Thu, Apr 2, 2020 at 3:05 AM maxxyme _ <[email protected]> wrote:
>
> OK Tatu, thanks for this start of an explanation.
>
> So basically what you're saying is the default `JsonCreator.Mode.DEFAULT`
> does not make the right "guess", despite what says the Javadoc, or maybe I
> wrongly understood it?
Your understanding is correct.
> public @interface JsonCreator
> {
> /**
> * Property that is used to indicate how argument(s) is/are bound for
> creator,
> * in cases there may be multiple alternatives. Currently the one case is
> that
> * of a single-argument creator method, for which both so-called
> "delegating" and
> * "property-based" bindings are possible: since
> * delegating mode can not be used for multi-argument creators, the only
> choice
> * there is "property-based" mode.
> * Check {@link Mode} for more complete explanation of possible choices.
> *<p>
> * Default value of {@link Mode#DEFAULT} means that caller is to use
> standard
> * heuristics for choosing mode to use.
> *
> * @since 2.5
> */
> public Mode mode() default Mode.DEFAULT;
>
> /**
> * Pseudo-mode that indicates that caller is to use default
> heuristics for
> * choosing mode to use. This typically favors use of delegating mode
> for
> * single-argument creators that take structured types.
> */
> DEFAULT,
>
> What does that mean "caller is to use default heuristics"?
That Jackson will try to guess correct mode, based on existence of:
1. Implicit name for one parameter Creator method has, and
2. There are indications of existing property for POJO (as inferred by
getter method)
There may be some other logic based on type of parameter (I'd have to
dig in code to verify), but these are 2 main pieces.
> Am I the caller (or caller code's coder...)? So am I supposed to effectively
> choose a specific mode and not use the DEFAULT one?
Caller here would mean Jackson databind's code that invokes creator method.
You as developer should specify mode if heuristics does not work, or,
if you just want to ensure right mode is used. I would recommend
latter.
>
> Another thing is that due to how I'm gonna structure my different classes (my
> example is just a simplified view) I can't/don't want to use @JsonProperty
>
> If I modify the example given above (in the SO link) as follows:
>
> public class Wrapper {
>
> private Inner inner;
>
> @JsonCreator(mode = Mode.DELEGATING)
> public Wrapper(Inner inner) {
> this.inner = inner;
> }
>
> public Inner getInner() {
> return inner;
> }
>
> }
>
> public class Inner {
>
> private String prop;
>
> @JsonCreator(mode = Mode.DELEGATING)
> public Inner(String prop) {
> this.prop = prop;
> }
>
> public String getProp() {
> return prop;
> }
>
> }
>
> public class JacksonDeserialization {
>
> private ObjectMapper om = new ObjectMapper();
>
> @Test // 1
> public void test_deserialization_emptyJson() throws
> JsonParseException, JsonMappingException, IOException {
> Wrapper read = om.readValue("{}", Wrapper.class);
> assertThat(read).isNotNull();
> assertThat(read.getInner()).isNull();
> }
>
> @Test // 2
> public void test_deserialization_innerIsEmpty() throws
> JsonParseException, JsonMappingException, IOException {
> Wrapper read = om.readValue("{\"inner\":{}}", Wrapper.class);
> assertThat(read).isNotNull();
> assertThat(read.getInner()).isNotNull();
> }
>
> @Test // 3
> public void test_deserialization_innerIsSet() throws
> JsonParseException, JsonMappingException, IOException {
> Wrapper read = om.readValue("{\"inner\":{\"prop\":\"42\"}}",
> Wrapper.class);
> assertThat(read).isNotNull();
> assertThat(read.getInner()).isNotNull();
> assertThat(read.getInner().getProp()).isEqualTo("42");
> }
>
> }
>
> I get three times the same exception:
>
> com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct
> instance of `Inner` (although at least one Creator exists): cannot
> deserialize from Object value (no delegate- or property-based Creator)
> at [Source: (String)"{}"; line: 1, column: 2]
That is right.
You are trying to bind a JSON Object into String value (delegation
means "bind JSON value, whatever it may be, into value of type I
have).
If content you get is JSON Object, you would either:
1. Specify "Properties" style: in this case there is no actual
property with matching name so `null` would be passed
2. Keep Delegating style, but bind to something that is compatible
with JSON Object: for example, Map<String, Object>
> com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct
> instance of `Inner` (although at least one Creator exists): cannot
> deserialize from Object value (no delegate- or property-based Creator)
> at [Source: (String)"{"inner":{}}"; line: 1, column: 2]
>
> com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct
> instance of `Inner` (although at least one Creator exists): cannot
> deserialize from Object value (no delegate- or property-based Creator)
> at [Source: (String)"{"inner":{"prop":"42"}}"; line: 1, column: 2]
>
> I still don't get what I'm doing wrong...
It seems to me that you are explaining what happens, instead of what
you are trying to achieve.
It would be easier to help if I knew what you try to do.
-+ Tatu +-
>
> maxxyme
>
> On Thursday, April 2, 2020 at 2:34:04 AM UTC+2, Tatu Saloranta wrote:
>>
>> On Wed, Apr 1, 2020 at 5:24 PM maxxyme _ <[email protected]> wrote:
>> >
>> > Hello,
>> >
>> > I already posted a question on SO but unfortunately didn't get any
>> > relevant answer (just one, and the guy told he's not keen on
>> > @JsonCreator...)
>> > Everything's detailed there (Java classes & call code):
>> > https://stackoverflow.com/questions/60132067/cant-properly-deserialize-json-using-jsoncreator-as-described-in-the-javadoc
>> >
>> > If someone's willing to point me to what's wrong with my class
>> > definitions, esp. with the 2 distinct exceptions:
>> > - I don't understand the 1st one (why doesn't it works...?)
>> > - For what I understand from the 2nd, Jackson tries to deserialize the
>> > whole JSON as for the Inner object... but why?
>>
>> Because there are 2 different possibilities of what might be expected.
>>
>> POJO1: expect JSON Object with same properties as POJO:
>>
>> public class Pojo1 {
>> public int x, y;
>>
>> @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
>> public Pojo(@JsonProperty("x") int x0, @JsonProperty("y") int y0) {
>> x = x0;
>> y = y0;
>> }
>> }
>>
>> JSON that can be read: {"x":1, "y":2}
>>
>> POJO 2: expect JSON _value_ that is used to construct POJO, but is not
>> property-based (often not JSON Object, but may be)
>>
>> public class Pojo2 {
>> int x, y;
>>
>> @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
>> public Pojo2(/* Note: NO property name */ int[] args) {
>> x = args[0];
>> y = args[1];
>> }
>> }
>>
>> JSON that can be read: [1, 2]
>>
>> -------------
>>
>> So far so good? One thing to note is that "delegating" style is only
>> applicable with just one parameter for constructor (or factory
>> method); whereas "properties" style works for any number.
>>
>> But this means that the special case of 1 argument constructor (or
>> factory method) can be ambiguous if `mode` is not specified. With
>> this:
>>
>> class NameBean {
>> private String name;
>>
>> @JsonCreator
>> public NameBean(String name) {
>> this.name = name;
>> }
>> }
>>
>> which JSON should this map to/from:
>>
>> { "name" : "Bob" }
>>
>> OR
>>
>> "Bob"
>>
>> ?
>>
>> There is no good way to figure out that I am aware of. Heuristics
>> fail; and as the answer at SO mentioned, parameter names for
>> constructors may or may not be included even on Java 8 (and were never
>> included before that; Jackson still only requires Java 7 for
>> databind).
>>
>> This is why you need to either add the `mode` property OR add explicit
>> `@JsonProperty` for constructor parameter if you want to force
>> Properties-style Creator.
>> And if conversely you want delegating style, mode.
>>
>> I hope this helps,
>>
>> -+ Tatu +-
>
> --
> You received this message because you are subscribed to the Google Groups
> "jackson-user" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/jackson-user/4e07dcf0-74f3-4bcb-b669-5a2f16256d2c%40googlegroups.com.
--
You received this message because you are subscribed to the Google Groups
"jackson-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/jackson-user/CAL4a10gdG72RNW%2B7w1KQSaELe5oRaheUU8NE2mrSmCJQvhQtSg%40mail.gmail.com.