users@jsonb-spec.java.net

[jsonb-spec users] [jsr367-experts] Re: Re: Java polymorphism support

From: Roman Grigoriadi <roman.grigoriadi_at_oracle.com>
Date: Wed, 23 Mar 2016 11:14:51 +0100

Hi,

@Romain - what you and Dmitry suggested with adapters, will actully work
in our case. We will have to to add a custom deserializer handler for a
"TypedWrapper<T>" (similar to collection / array or reflection
deserializers), which will read a class property first and on
encountering a json object for T will create a correct type instance.
The only condition would be, that class property must appear before
inner instance in json object for "TypedWrapper<T>" as Eugen suggested:

{"classProperty":"com.foo.Dog","instance":{...}}

Adapters will than work in a common way to extract T from TypedWrapper
and set it to a class model be it a field or a collection/array.

I would also add required enumeration of allowed subtypes into TypedAdapter:

class TypedAdapter<T> implements JsonbAdapter<T, TypedWrapper<T>> {
     TypedAdapter(Class[] allowedSubtypes) {...}
     ...
}

So we don't allow loading of any arbitrary class suggested by incoming
json in case of not trusted third party.

@Romain - adding support for custom serializers / deserializers in
fashion of fasterxml with access to JsonGenerator/JsonParser would also
solve the problem in similar fashion, but it would be more complicated
for a client to implement logic driven by JsonParser events, than by
providing predefined adapter.

Thanks,
Roman

On 03/21/2016 09:36 PM, Romain Manni-Bucau wrote:
> Hi Dmitry,
>
> was my thought as well but it doesn't really work cause in adapter you
> can't relaunch the deserialization chain with the same jsonb instance.
> The serialization side is the easy one but the deserialization makes
> adapters API not enough to have a consistent behavior accross the
> deserialization. Making the adapter "JsonbAware" would work. We can
> probably use @Inject (CDI if there or just the JSR 330 as an optional
> dependency if not)
>
> Side note: I'd like we enable this impl but we don't provide them in
> the spec since it is likely dependent on your use case and saw cases
> where type was not enough and we will likely not handle all possible
> models.
>
>
>
> Romain Manni-Bucau
> @rmannibucau
> http://www.tomitribe.com
> http://rmannibucau.wordpress.com
> https://github.com/rmannibucau
>
> 2016-03-21 21:27 GMT+01:00 Dmitry Kornilov <dmitry.kornilov_at_oracle.com
> <mailto:dmitry.kornilov_at_oracle.com>>:
>
> Hi,
>
> This topic has been discussed already. Current version of the spec
> is clear about polymorphism: /“//Deserialization into polymorphic
> types is not supported by default mapping. [JSB-3.8-1]”. /The spec
> is almost finished and I am strongly against going back and
> changing some basic things now.
>
> On the other hand I understand that this use case exists. There is
> a possible workaround to it using adapters. Sorry if there are any
> mistakes, I am writing code without checking.
> /
> /
> We can create a generic wrapper to any type holding its type info
> like this:
>
> class TypedWrapper<T> {
> String type;
> T obj;
> }
>
> We can create a generic adapter which converts type to wrapper and
> vice versa. I am not putting a full implementation here, I hope
> that the idea is clear:
>
> class TypedAdapter implements JsonbAdapter<T, TypedWrapper<T>> {
> TypedWrapper adaptToJson(T obj) {
> TypedWrapper<T> t = new TypedWrapper<T>();
> t.type = obj.getClass().getName();
> t.obj = obj;
> return t;
> }
>
> T adaptFromJson(TypedWrapper<T> t) {
> // Use reflection API to create an instance of object
> based on t.type field value
> }
> }
>
> Now lets imagine that we have the following classes:
>
> class Animal {
> }
>
> class Dog extends Animal {
> }
>
> class Cat extends Animal {
> }
>
> class Foo {
> Animal animal;
> }
>
> To properly handle serialization/deserialization of Foo we just
> need to create an adapter which extends TypedAdapter from above
> and annotate /animal/ field:
>
> class AnimalAdapter extends TypedAdapter<Animal,
> TypedWrapper<Animal>> {
> }
>
> class Foo {
> @JsonbTypeAdapter(AnimalAdapter.class)
> Animal animal;
> }
>
> We can include TypedWrapper and TypedAdapter in reference
> implementation.
>
> Thanks,
> Dmitry
>
>
>
>> On 21 Mar 2016, at 19:31, Eugen Cepoi <cepoi.eugen_at_gmail.com
>> <mailto:cepoi.eugen_at_gmail.com>> wrote:
>>
>> Not sure if it should or not be part of jsonb. Though it is very
>> likely people will want that feature. On the other side, it is a
>> good way to have different impls distinguish them self by
>> providing such advanced features (I mean as part of the impl and
>> not at all present in the spec). In that case I would find it
>> fine to have people configure directly the impl api.
>>
>> When we think polymorphism, at first it seems enough to handle it
>> only for json objects, but it can also happen for other types,
>> esp. ones that are serialized as literals. Then where do you
>> store the type? Same for json arrays. This is just one aspect of
>> the introduced complexity when one wants to handle polymorphism.
>> The produced json starts getting more and more ugly.
>>
>> Then comes the moment when you want to change the name of the
>> serialized property or the format you use.
>>
>> In some other situations (depending on how it has been
>> implemented) you might want to have access to a dom structure
>> that holds the properties so you can extract from them what is
>> related to the type and then "convert" that structure to the
>> target type. This happens if you allow the type property to
>> appear in any order.
>>
>> There are many other things that one could want for polymorphic
>> support which would make all that even more complex.
>>
>>
>> In genson I first handled it only for what gets serialized as a
>> json object and imposed that the type property must appear first
>> in the json (so we don't need to read the full structure before
>> knowing the target type). Then recently I added support for all
>> types (literal and array) by wrapping them in a json object (yeah
>> not very nice, but has the merit of being relatively simple).
>>
>>
>>
>> 2016-03-21 2:22 GMT-07:00 Sebastian Daschner
>> <java_at_sebastian-daschner.de <mailto:java_at_sebastian-daschner.de>>:
>>
>> Hi guys,
>>
>> The idea was that this "javaType" (name is subject to change)
>> wouldn't be included in all cases. Default behaviour should
>> be to serialize as small / easy as possible. Only if the type
>> information is needed to determine the actual type, an
>> @JsonbTypeInfo annotation could be added to the corresponding
>> type. Configuring a "forceTypeInfo" boolean for the
>> programmatic JSONB config could be possible though.
>>
>> @Przemyslaw:
>> Even if you solely use DTOs for serialization (I won't agree
>> that that's a must for all situations) you can still have
>> situations with subclasses and more complex hierarchies -- we
>> had that case in several projects, also when using transfer
>> objects to map the domain objects. "Flattening" the type
>> hierarchies as workaround is not always desired.
>>
>> But the longer I think about your approach that enums could
>> be used to derive the type information (like JPA, yes) the
>> more I like this idea as it would be more generic (more
>> difficult / more effort to configure, but easier to apply to
>> different technologies). We should investigate further (with
>> some examples) what approach could be the best fit for JSONB.
>>
>> But I highly recommend that some functionality to determine
>> polymorphic types should be included in the spec.
>>
>>
>> @Romain:
>> The spec should in fact require the impl to check for
>> "suitable" types then, i.e. only types to which the actual
>> property type can be assigned to should be allowed, etc.
>>
>> I like your idea to add a "Wrapper" to unwrap instances but
>> let JSONB still do the (de-)serialization. This could be
>> helpful for more control how the containing type (the List in
>> my example) should be instanciated. But it doesn't fully
>> solve the polymorphism example in all cases, as the
>> implementation of the actual type (Customer or VipCustomer)
>> can still not determined from the nested JSON objects if
>> there is no such thing as a JSON meta type information.
>>
>>
>> Cheers,
>> Sebastian
>> On 03/21/2016 08:32 AM, Przemyslaw Bielicki wrote:
>>> Hi,
>>>
>>> I'm not a fan of the original idea (and it's an euphemism).
>>> Remember that JSONB is used to serialize/deserialize DTOs,
>>> not domain objects. You should have an additional adapter
>>> layer to convert DTOs into application's business
>>> model/domain objects and back.
>>> Polymorphism is broken (yeah, it's another topic and more
>>> philosophic) and I would discourage its application in DTO
>>> layer. If you have to, you can use an enum to mark what's
>>> the target class (check JPA for additional inspirations).
>>> "//javaType": "x.y.z" is really bad idea IMO - what if you
>>> use the same JSON to communicate with applications developed
>>> in .net, python, php, c++ at the same time? (yes, it
>>> happens). Would you define a type for each platform?
>>>
>>> If you use DTOs in business layer, you're doing something
>>> wrong :)
>>>
>>> @Romain, your idea looks interesting, can you show and
>>> example or deserialization process where polymorphism is
>>> involved?
>>>
>>> Cheers,
>>> Przemyslaw
>>>
>>> On Sun, Mar 20, 2016 at 10:52 PM, Romain Manni-Bucau
>>> <rmannibucau_at_tomitribe.com
>>> <mailto:rmannibucau_at_tomitribe.com>> wrote:
>>>
>>> Hi guys
>>>
>>> Why not allowing adapters to wrap and unwrap instances
>>> but keep serializer mecanism. For serialization no issue
>>> but for deserialization we need to switch the
>>> deserialized type and then post process the instance -
>>> or do it automatically with an Unwrappable interface?
>>>
>>> Overall idea for this particular use case would be:
>>>
>>> X ----TypedWrapper(X)----> JSON
>>> JSON----TypedWrapper.class----> TypedWrapped
>>> ----unwrap----> X
>>>
>>> Parts in arrows are adapter/jsonb integration.
>>>
>>> An alternative is to provide to adapters the stream and
>>> jsonp instances to do it themself with probably a mapper
>>> reference to re(de)serialise an object directly.
>>>
>>> Wdyt?
>>>
>>> ---------- Message transféré ----------
>>> De : "Romain Manni-Bucau" <rmannibucau_at_tomitribe.com
>>> <mailto:rmannibucau_at_tomitribe.com>>
>>> Date : 19 mars 2016 21:49
>>> Objet : Re: [jsonb-spec users] Java polymorphism support
>>> À : <users_at_jsonb-spec.java.net
>>> <mailto:users_at_jsonb-spec.java.net>>
>>> Cc :
>>>
>>> Hi Sebastian
>>>
>>> I am not fan of that - Mark knows ;) - and in particular
>>> now I'm sure we shouldnt do it: the 0-day vulnerability
>>> is still there and you open the door to the same issue
>>> or a complicated config adding this feature in something
>>> as generic as jsonb.
>>>
>>> Le 19 mars 2016 21:28, "Sebastian Daschner"
>>> <java_at_sebastian-daschner.de
>>> <mailto:java_at_sebastian-daschner.de>> a écrit :
>>>
>>> Hi experts,
>>>
>>> I don't know whether this has been discussed in the
>>> mailing list before
>>> but a needed functionality would be to specify the
>>> Java type of
>>> properties in the serialized JSON.
>>>
>>> As JSON doesn't standardize comments or other
>>> "attributes" (like XML
>>> does) a "magic property" could be added.
>>>
>>> Please see the proposal (explored by Mark Struberg,
>>> Reinhard Sandtner
>>> and myself) on my blog:
>>> https://blog.sebastian-daschner.com/entries/json_mapping_polymorphism_support
>>>
>>> WDYT?
>>>
>>>
>>> Cheers,
>>> Sebastian
>>>
>>>
>>
>>
>
>