users@jsonb-spec.java.net

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

From: Romain Manni-Bucau <rmannibucau_at_tomitribe.com>
Date: Wed, 23 Mar 2016 11:20:25 +0100

Hi Roman,

can you fill some "..." please? typically at the moment "I know the type is
X". How with an adapter do you instantiate X and then let jsonb populate it
(without having another Jsonb instance)? That was the main missing part of
the puzzle.


Romain Manni-Bucau
@rmannibucau
http://www.tomitribe.com
http://rmannibucau.wordpress.com
https://github.com/rmannibucau

2016-03-23 11:14 GMT+01:00 Roman Grigoriadi <roman.grigoriadi_at_oracle.com>:

> 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>:
>
>> 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>
>> 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>
>> :
>>
>>> 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>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>
>>>> rmannibucau_at_tomitribe.com>
>>>> Date : 19 mars 2016 21:49
>>>> Objet : Re: [jsonb-spec users] Java polymorphism support
>>>> À : < <users_at_jsonb-spec.java.net>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>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>
>>>>> https://blog.sebastian-daschner.com/entries/json_mapping_polymorphism_support
>>>>>
>>>>> WDYT?
>>>>>
>>>>>
>>>>> Cheers,
>>>>> Sebastian
>>>>>
>>>>>
>>>
>>>
>>
>>
>
>