Hello,

Any idea regarding my issue?

Regards,
maxxyme

On Friday, April 3, 2020 at 6:20:13 PM UTC+2, maxxyme _ wrote:
>
> OK then...
>
> What I'm trying to achieve is a generic wrapper class like this:
>
>     public class Wrapper<T> {
>    
>         private String id;
>         // maybe other common fields...
>         private T t;
>    
>         @JsonCreator(...?)
>         public Wrapper(T t) {
>             this.t = t;
>         }
>    
>         public String getId() {
>             return id;
>         }
>    
>         public void setId(String id) {
>             this.id = id;
>         }
>    
>         // other getters & setters for the common fields...
>    
>         public T get() {
>             return t;
>         }
>    
>     }
>
> ... in order to read an external API I have no control over.
>
> e.g. the JSON comes like:
>
> { "id": "3aeb3f363", "foo": { "fooProp1": "fooV1", "fooProp2": "fooV2" } }
>
> to be mapped into a Wrapper<Foo> with Foo :
>
>     public class Foo {
>    
>         private String fooProp1;
>         private String fooProp2;
>       
>         // getters & setters
>     }
>
> or similar:
>
> { "id": "3aeb3f363", "bar": { "barProp1": "barV1", "barProp2": "barV2", 
> "barProp3": "barV3" } }
>
> to be mapped into a Wrapper<Bar> with Bar :
>
>     public class Bar {
>    
>         private String barProp1;
>         private String barProp2;
>         private String barProp3;
>       
>         // getters & setters
>     }
>
> ...and so on.
>
> There are numerous "entites" like Foo or Bar (each having their own 
> properties & Java class).
>
> Sometimes, you can even have nothing, e.g.:
> { "id": "3aeb3f363" }
>
> which should be mapped to a class of type Wrapper<Void> (the 
> java.lang.Void one).
> And in that particular case, the variable t (from Wrapper) would be null; 
> in fact I'm trying to mimic the java.util.Optional class which can not be 
> extended to add extra properties.
>
> All is done by defining each API call in a Java interface as a method 
> signature, OpenFeign build an entire HTTP client and handles the whole 
> process of calling Jackson to serialize the parameters in order to send the 
> HTTP request then deserialize the HTTP response.
>
> public interface MyAPI {
>
>     Wrapper<Foo> getSomeFoo(/* some parameters for the 'Foo' API call */);
>
>     Wrapper<Bar> getSomeBar(/* some parameters for the 'Foo' API call */);
>
>     Wrapper<Void> doSomething(/* some parameters for another API call */);
>
> }
>
> In a Spring context, the MyAPI is autowired, e.g.
>
> @Autowired
> private MyAPI myAPI;
>
> then you call:
>
> Wrapper<Foo> wrapperFoo = myAPI.getSomeFoo(/* with excepted parameters to 
> make the API call */);
>
> And I know this would work because I partly implemented all that, and I 
> tried once to debug the call, and at some point before calling the 
> constructor public Wrapper(T t), Jackson had the correct Java class for the 
> T parameter (e.g. Foo.class)
> The last thing I don't really understand is how to "map" the value of the 
> field having the "name" of the "entity" (e.g. "foo" or "bar"), maybe with a 
> custom deserializer...?
> That's why I was trying to build a simpler example without the generic 
> part in order to understand how might "work" the @JsonCreator annotation.
>
> Hoping I made myself clear?
>
> Regards,
> maxxyme
>
> On Thursday, April 2, 2020 at 7:07:25 PM UTC+2, Tatu Saloranta wrote:
>
>> 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/95e03043-300c-42f1-a830-c4f49d5a6681%40googlegroups.com.

Reply via email to