users@jsonb-spec.java.net

[jsonb-spec users] [jsr367-experts] Re: Re: Re: JSONB De/Serializers proposal

From: Romain Manni-Bucau <rmannibucau_at_tomitribe.com>
Date: Fri, 29 Apr 2016 15:28:47 +0200

Le 29 avr. 2016 15:03, "Dmitry Kornilov" <dmitry.kornilov_at_oracle.com> a
écrit :
>
>
> 1.
>
> Imagine that we have a serialiser defined as this:
>
> JsonbDeserializer<Animal>
>
> There is a generic Dog class extending Animal interface:
> Dog<T> extends Animal
>
> And the actual object that we are deserialising is:
> Dog<WhatEver>
>
> ParametrizedType representing Dog<WhatEver> type will be passed as rtType
parameter. I suppose it’s runtime type, not static.
>

You can get Object for generic converters so static type would be better in
such a case.

> 2.
>
> I am not sure. Maybe we need it. Roman, please answer.
>
> 3.
>
> I didn’t understand what are you talking about. The best would be if you
modify our API code from the previous email so we can better understand
what you mean.
>

Basically bring back Jsonb API for a subobject us the idea.
readObject(Type) typically or readObject(Type, attribute). Idea is to use
Object instead of JsonXXX everywhere as first class citizen. We can provide
an unwrap(JsonXxx) if user wants to go lower level bit i dont see it useful
yet.

> 4.
>
> We don’t use optional constraints in our code. We are sort of old school,
you know. :)
> But the idea is reasonable. Let us discuss it.
>
> Thanks,
> Dmitry
>
>
>> On 29 Apr 2016, at 13:07, Romain Manni-Bucau <rmannibucau_at_tomitribe.com>
wrote:
>>
>> Hi Roman,
>>
>> looks better, few comments:
>>
>> 1- @param rtType Runtime type of T think it should be static type more
than runtime no?
>> 2- do we want readStructure(Type)?
>> 3- more generally we should get mapped versions of
[read|write][Object|Array|Structure] (readObject(Type) typically) otherwise
we kind of loose the mapping level jsonb should be at, no?
>> 4- [open question] do we go with optional for convertion and get rid of
supports method: convert(.../*supported case*/).orElse(..../* not supported
*/)
>>
>> Main point is 3, once solved I think we are good.
>>
>>
>> Romain Manni-Bucau
>> @rmannibucau
>> http://www.tomitribe.com
>> http://rmannibucau.wordpress.com
>> https://github.com/rmannibucau
>>
>> 2016-04-29 12:23 GMT+02:00 Dmitry Kornilov <dmitry.kornilov_at_oracle.com>:
>>>
>>> Hi,
>>>
>>> Romain, thanks for your suggestions. Below you can find a fixed version
of serialisers API. What’s changed:
>>>
>>> JsonbGenerator/JsonbParser renamed to JsonbWriter/JsonbReader.
>>> JsonbWriter and JsonbReader are not extended from the corresponding
JSONP classes as Romain suggested.
>>> Some JSON processing methods added to JsonbWriter and JsonbReader.
Possible we will need to add some more. Your suggestions?
>>> Introduced rtType parameter in JsonbDeserializer.deserialize method. It
holds a runtime type of an object.
>>>
>>> WDYT?
>>>
>>> Dmitry, Roman
>>>
>>>
>>> public interface JsonbSerializer<T> {
>>>
>>> /**
>>> * @param obj Object to process
>>> * @param writer Low level JSON writer
>>> * @param ctx Jsonb mapper
>>> */
>>> void serialize(T obj, JsonbWriter writer, SerializationContext ctx);
>>>
>>> }
>>>
>>> public interface JsonbDeserializer<T> {
>>>
>>> /**
>>> * @param parser Low level JSON reader
>>> * @param ctx Jsonb mapper
>>> * @param rtType Runtime type of T
>>> */
>>> T deserialize(JsonbReader reader, DeserializationContext ctx, Type
rtType);
>>> }
>>>
>>>
>>> /**
>>> * Jsonb wrapper around JSONP generator + helper methods.
>>> */
>>> public interface JsonbWriter {
>>>
>>> // methods delegated from JSONP generator
>>> // ...
>>>
>>> /**
>>> * Writes a JSONP object.
>>> */
>>> void writeObject(javax.json.JsonObject obj);
>>>
>>> /**
>>> * Writes a JSONP array.
>>> */
>>> void writeArray(javax.json.JsonArray arr);
>>>
>>>
>>> /**
>>> * Writes a JSONP object.
>>> */
>>> void writeStructure(javax.json.JsonStructure struct);
>>> }
>>>
>>> /**
>>> * Jsonb wrapper around JSONP parser + helper methods.
>>> */
>>> public interface JsonbReader {
>>>
>>> // methods delegated from jsonp parser
>>> // ...
>>>
>>> /**
>>> * Reads a JSONP object.
>>> */
>>> javax.json.JsonObject readObject();
>>>
>>> /**
>>> * Reads a JSONP array.
>>> */
>>> javax.json.JsonArray readArray();
>>> }
>>>
>>> /**
>>> * Jsonb mapper used for serialization.
>>> */
>>> public interface SerializationContext {
>>>
>>> /**
>>> * Serializes arbitrary object to JSON with JSONB, using current
{_at_link javax.json.stream.JsonGenerator} instance.
>>> */
>>> <T> void serialize(String key, T object);
>>>
>>> /**
>>> * Checks if type is supported for conversion to string by JSONB.
>>> */
>>> boolean supportsConversion(Class<?> clazz);
>>>
>>> /**
>>> * Converts string value into provided type. String value has to be
single JSON value, not a part
>>> * of a JSON document representing JSON object / array.
>>> */
>>> <T> String convertDefault(T obj);
>>> }
>>>
>>> /**
>>> * Jsonb deserialization mapper.
>>> */
>>> public interface DeserializationContext {
>>> /**
>>> * Deserializes JSON stream into instance of provided class with
JSONB using {_at_link javax.json.stream.JsonParser}.
>>> * JsonParser cursor have to be at KEY_NAME before START_OBJECT to
call this method.
>>> */
>>> <T> T deserialize(Class<T> clazz);
>>>
>>> /**
>>> * Similar as above for generic types.
>>> */
>>> <T> T deserialize(TypeInfo<T> typeInfo);
>>>
>>> /**
>>> * Checks if type is supported for conversion from string by JSONB.
>>> */
>>> boolean supportsConversion(Class<?> toClass);
>>>
>>> /**
>>> * Converts string value into provided type. String value has to be
single JSON value, not a part
>>> * of a JSON document representing JSON object.
>>> */
>>> <T> T convertDefault(Class<T> toClass, String value);
>>>
>>> }
>>>
>>>
>>>
>>>
>>>> On 28 Apr 2016, at 16:43, Romain Manni-Bucau <rmannibucau_at_tomitribe.com>
wrote:
>>>>
>>>>
>>>> 2016-04-28 16:09 GMT+02:00 Roman Grigoriadi <
roman.grigoriadi_at_oracle.com>:
>>>>>
>>>>> Inline, thanks, Roman
>>>>>
>>>>> On 04/28/2016 03:31 PM, Romain Manni-Bucau wrote:
>>>>>>
>>>>>>
>>>>>> 2016-04-28 15:26 GMT+02:00 Roman Grigoriadi <
roman.grigoriadi_at_oracle.com>:
>>>>>>>
>>>>>>> Thought main reason for (de)seserializers is to provide such a low
level API to drive the JSONP by hand. The drawback is that user must use
Parser/Generator judiciously, because it is shared with JSONB runtime. If
we hide it completely, what methods would JsonbXXX interfaces have? You
cant do anything like getValue(String key), because once parser is advanced
it cannot move back. How about dropping JsonbXXX completly and using JSONP
Generator / Parser along with (De)SerializationContexts?
>>>>>>>
>>>>>>
>>>>>> Well that's one of the reason why we shouldn't expose jsonp. So here
what I had in mind:
>>>>>> - expose all method of jsonp but in Jsonb API
>>>>>
>>>>> How does hiding JSONP and providing 1:1 JSONB interface help against
biasing runtime with inconsistently advanced parser? What are other
pitfalls if we expose JSONP in deserializers?
>>>>
>>>>
>>>> As a user you see the json structure not the stream so having a parser
is an implementation detail you don't need (you need a reader more than a
parser to be honest). Then up to the jsonb runtime to bufferise what needs
to be bufferised to keep its parser up to date.
>>>>
>>>> The other pitfalls I see is to not be able to present a consistent
view to the users and open the unsafe usage with next jsonp releases (if
you use jsonp N+1 with jsonb N generations).
>>>>
>>>>>
>>>>>
>>>>>> - the Jsonb impl is free to use markable parser (able to
rewind/forward if needed) to make user happy whatever he does
>>>>>
>>>>> Is it? What about custom JSONP Providers in JsonbConfig? Aren't
implementations supposed to use only JSONP API as their backend?
>>>>
>>>>
>>>> Didn't follow. Maybe my comment was unclear: the markable impl would
be an internal of jsonb runtime.
>>>>
>>>>>
>>>>>
>>>>>> - Jsonb versions should get object/array handling in addition
>>>>>>>
>>>>>>> so deserializer method may look like:
>>>>>>>
>>>>>>> T deserialize(JsonParser parser, //JSONP parser
>>>>>>> DeserializationContext context) //has
JSONB mapper related methods
>>>>>>>
>>>>>>> or
>>>>>>>
>>>>>>> T deserialize(DeserializationContext context)
>>>>>>> and add
>>>>>>> JsonParser DeserializationContext#getParser() ?
>>>>>>>
>>>>>>> According to generics part, did you mean that you would not be able
to resolve X if Deserializer is defined as MyDeserializer<MyPojo<X>> and
you want JSONB to pass a type of X into deserializer? So that
deserialize(...) method would be added by argument Type typeOfX ?
>>>>>>>
>>>>>>
>>>>>> If I got it right yes. (De)Serializer can be generic and loose the
type at instantiation (or not even have it if in a framework where new
MYDeser<X>(){} wouldnt work). So the minimum the runtime can do is to say
"hey, this is the best type I can find for what you have to return".
>>>>>
>>>>> It didn't make much sense to me to declare a serializer in such way.
If you declare it like "class <T> MyDeser<MyPojo<T>> you still have to
provide some T during instantiation (will be erased). If T is declared in a
class, which creates instance of a serializer, how will JSONB know what is
it bound to in runtime?
>>>>>
>>>>
>>>> You have the model and know you need to get an Animal for instance.
Then if it is an interface it will likely not work and the deserializer
will need to take the impl from somewhere else but at least as a runtime
you did your best and you would work in most of cases.
>>>>
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> Thanks,
>>>>>>> Roman
>>>>>>>
>>>>>>>
>>>>>>> On 04/27/2016 03:16 PM, Romain Manni-Bucau wrote:
>>>>>>>>
>>>>>>>> 2016-04-27 15:12 GMT+02:00 Roman Grigoriadi <
roman.grigoriadi_at_oracle.com>:
>>>>>>>>>
>>>>>>>>> The difference between JsonbXXX and (De)SerContext is separation
of processing logic, former has methods for driving JSONP, latter uses
JSONB on top of it.
>>>>>>>>>
>>>>>>>>
>>>>>>>> I get it but the main purpose of the mapper if IMO to hide the
parsing part so I would keep it but not as a main concept. That said I'm ok
with that if I'm alone on this side ;).
>>>>>>>>
>>>>>>>>>
>>>>>>>>> I don't get whats wrong with runtime type, for serialization.
Have you noticed
>>>>>>>>>
>>>>>>>>> <T> T deserialize(TypeInfo<T> typeInfo);
>>>>>>>>>
>>>>>>>>> method? It will deserialize generic types.
>>>>>>>>>
>>>>>>>>
>>>>>>>> Sure but how do you do a new X() if you don't know X()? so the
deserialize should get the static type somehow. <T> T deserialize() is an
option but guess having the type in the signature allows to not instantiate
it if not needed.
>>>>>>>>
>>>>>>>>>
>>>>>>>>> Roman.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On 04/27/2016 02:43 PM, Romain Manni-Bucau wrote:
>>>>>>>>>>
>>>>>>>>>> Hi Roman,
>>>>>>>>>>
>>>>>>>>>> Few points but globally I think we move in the right direction!
>>>>>>>>>>
>>>>>>>>>> - "extends JsonXXX" I'd replace the extends by a delegation to
keep the fluent API typed (= return type is JsonbXXX and not JsonXXX) and
control over jsonb API.
>>>>>>>>>> - I don't fully get the difference between context and JsonbXXX,
i'd merge them (otherwise we would just using jsonp API but as mentionned
in previous point it has the drawback to loose all the control.
>>>>>>>>>> - deserialization: we still need the type found by the runtime
(reflection) otherwise we can't allow generic serializers at all
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Romain Manni-Bucau
>>>>>>>>>> @rmannibucau
>>>>>>>>>> http://www.tomitribe.com
>>>>>>>>>> http://rmannibucau.wordpress.com
>>>>>>>>>> https://github.com/rmannibucau
>>>>>>>>>>
>>>>>>>>>> 2016-04-27 14:33 GMT+02:00 Roman Grigoriadi <
roman.grigoriadi_at_oracle.com>:
>>>>>>>>>>>
>>>>>>>>>>> Hi experts,
>>>>>>>>>>>
>>>>>>>>>>> following previous discussions for including de/serializers in
JSONB along with adapters, here is proposal for interfaces in JSONB API:
>>>>>>>>>>>
>>>>>>>>>>> public interface JsonbSerializer<T> {
>>>>>>>>>>> void serialize(T obj, JsonbGenerator generator,
SerializationContext ctx);
>>>>>>>>>>> }
>>>>>>>>>>>
>>>>>>>>>>> public interface JsonbDeserializer<T> {
>>>>>>>>>>> T deserialize(JsonbParser parser, DeserializationContext
ctx);
>>>>>>>>>>> }
>>>>>>>>>>>
>>>>>>>>>>> /**
>>>>>>>>>>> * Jsonb decorator for JSONP generator. Includes validation for
writing END_OBJECT / END_ARRAY checking level of closed JSON structure.
>>>>>>>>>>> * Helper generator methods may be added here.
>>>>>>>>>>> */
>>>>>>>>>>> public interface JsonbGenerator extends JsonGenerator {
>>>>>>>>>>> }/**
>>>>>>>>>>> * Jsonb decorator for JSONP parser. Includes validation of
cursor position after leaving deserializer, alters hasNext() behavior.
>>>>>>>>>>> * Helper parser methods may be added here.
>>>>>>>>>>> */
>>>>>>>>>>> public interface JsonbParser extends JsonParser {
>>>>>>>>>>> }
>>>>>>>>>>>
>>>>>>>>>>> public interface SerializationContext {
>>>>>>>>>>>
>>>>>>>>>>> /**
>>>>>>>>>>> * Serializes arbitrary object to JSON with JSONB, using
current {_at_link javax.json.stream.JsonGenerator} instance.
>>>>>>>>>>> */
>>>>>>>>>>> <T> void serialize(String key, T object);
>>>>>>>>>>>
>>>>>>>>>>> /**
>>>>>>>>>>> * Checks if type is supported for conversion to string by
JSONB.
>>>>>>>>>>> */
>>>>>>>>>>> boolean supportsConversion(Class<?> clazz);
>>>>>>>>>>>
>>>>>>>>>>> /**
>>>>>>>>>>> * Converts string value into provided type. String value
has to be single JSON value, not a part
>>>>>>>>>>> * of a JSON document representing JSON object / array.
>>>>>>>>>>> */
>>>>>>>>>>> <T> String convertDefault(T obj);
>>>>>>>>>>> }public interface DeserializationContext {
>>>>>>>>>>>
>>>>>>>>>>> /**
>>>>>>>>>>> * Deserializes JSON stream into instance of provided class
with JSONB using {_at_link javax.json.stream.JsonParser}.
>>>>>>>>>>> * JsonParser cursor have to be at KEY_NAME before
START_OBJECT to call this method.
>>>>>>>>>>> */
>>>>>>>>>>> <T> T deserialize(Class<T> clazz);
>>>>>>>>>>>
>>>>>>>>>>> /**
>>>>>>>>>>> * Similar as above for generic types.
>>>>>>>>>>> */
>>>>>>>>>>> <T> T deserialize(TypeInfo<T> typeInfo);
>>>>>>>>>>>
>>>>>>>>>>> /**
>>>>>>>>>>> * Checks if type is supported for conversion from string
by JSONB.
>>>>>>>>>>> */
>>>>>>>>>>> boolean supportsConversion(Class<?> toClass);
>>>>>>>>>>>
>>>>>>>>>>> /**
>>>>>>>>>>> * Converts string value into provided type. String value
has to be single JSON value, not a part
>>>>>>>>>>> * of a JSON document representing JSON object.
>>>>>>>>>>> */
>>>>>>>>>>> <T> T convertDefault(Class<T> toClass, String value);
>>>>>>>>>>>
>>>>>>>>>>> }
>>>>>>>>>>>
>>>>>>>>>>> JsonbParser / JsonbGenerator objects are supposed to decorate
JSONP behavior.
>>>>>>>>>>> For example in case of JsonParser hasNext() call may be altered
to return false if currently deserialized object has ended in the stream.
Helper methods (like nextKey(String), isKey(String), etc for json
processing may be included here.
>>>>>>>>>>>
>>>>>>>>>>> In addition to Generators/Parsers, (De)SerializationContexts
are intended to provide JSONB functionality such as (de)serialization of
arbitrary java types, conversion to/from string of known types such are
Dates or Numbers.
>>>>>>>>>>> We could also add some insight into introspected customization
from JSONB annotations based on a Class and property name.
>>>>>>>>>>>
>>>>>>>>>>> An example of how implementation of such (de)serializer may
look:
>>>>>>>>>>>
>>>>>>>>>>> public class CrateSerializer implements JsonbSerializer<Crate> {
>>>>>>>>>>>
>>>>>>>>>>> @Override
>>>>>>>>>>> public void serialize(Crate obj, JsonbGenerator generator,
SerializationContext ctx) {
>>>>>>>>>>> generator.write("crateStr", "REPLACED");
>>>>>>>>>>> ctx.serialize("crateInner", obj.crateInner);
>>>>>>>>>>> ctx.serialize("crateInnerList", obj.crateInnerList);
>>>>>>>>>>> generator.write("date-converted", dateValue);
>>>>>>>>>>> }
>>>>>>>>>>> }
>>>>>>>>>>>
>>>>>>>>>>> public class CrateDeserializer implements
JsonbDeserializer<Crate> {
>>>>>>>>>>>
>>>>>>>>>>> @Override
>>>>>>>>>>> public Crate deserialize(JsonbParser jsonParser,
DeserializationContext ctx) {
>>>>>>>>>>> Crate crate = new Crate();
>>>>>>>>>>>
>>>>>>>>>>> while (jsonParser.hasNext()) {
>>>>>>>>>>> JsonParser.Event next = jsonParser.next();
>>>>>>>>>>>
>>>>>>>>>>> if (next.equals(JsonParser.Event.KEY_NAME) &&
jsonParser.getString().equals("crateInner")) {
>>>>>>>>>>> //invokes JSONB processing for a CrateInner as
a root type with "shared" instance of JsonParser
>>>>>>>>>>> //may invoke other serializer or adapter
instances inside
>>>>>>>>>>> crate.crateInner =
ctx.deserialize(CrateInner.class);
>>>>>>>>>>> next = jsonParser.next();
>>>>>>>>>>> }
>>>>>>>>>>> if (next.equals(JsonParser.Event.KEY_NAME) &&
jsonParser.getString().equals("crateInnerList")) {
>>>>>>>>>>> //invokes JSONB processing for a
List<CrateInner> as a root type with "shared" instance of JsonParser
>>>>>>>>>>> //may invoke other serializer or adapter
instances inside
>>>>>>>>>>> crate.crateInnerList = ctx.deserialize(new
TypeToken<List<CrateInner>>() {});
>>>>>>>>>>> }
>>>>>>>>>>> if (next.equals(JsonParser.Event.KEY_NAME) &&
jsonParser.getString().equals("date-converted")) {
>>>>>>>>>>> jsonParser.next();
>>>>>>>>>>> //don't have context of processing here, no
annotation customizations applied.
>>>>>>>>>>> Date converted = ctx.convertDefault(Date.class,
jsonParser.getString());
>>>>>>>>>>> crate.date = converted;
>>>>>>>>>>> }
>>>>>>>>>>>
>>>>>>>>>>> }
>>>>>>>>>>>
>>>>>>>>>>> return crate;
>>>>>>>>>>> }
>>>>>>>>>>> }
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> @Romain, this is similar to what you have suggested first time.
>>>>>>>>>>>
>>>>>>>>>>> In addition JsonbAdapters may be used to convert From / To
JsonObject or JsonArray, but auto conversion between unknown JavaTypes and
JsonStructure will not be supported, at least in first version of a spec.
>>>>>>>>>>> WDYT?
>>>>>>>>>>>
>>>>>>>>>>> Thanks,
>>>>>>>>>>> Roman
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>