Hi,

(Other comments in line).

I added a new feature:

===
Name: Neo-JSON-Core-SvenVanCaekenberghe.31
Author: SvenVanCaekenberghe
Time: 30 December 2015, 12:31:59.303349 pm
UUID: fb235526-3c04-4e5f-a543-9a7e9eaaac2a
Ancestors: Neo-JSON-Core-SvenVanCaekenberghe.30

New #allowNil option to Object mapping so that null values are accepted and 
returned as nil when reading a schema using #nextAs: (off by default)

Added NeoJSONObjectMapping>>#allowNil
Added NeoJSONReaderTests>>#testAllowNil
===
Name: Neo-JSON-Tests-SvenVanCaekenberghe.31
Author: SvenVanCaekenberghe
Time: 30 December 2015, 12:32:13.455009 pm
UUID: 650b4ecf-c9d9-40a5-a988-e4e3801faa42
Ancestors: Neo-JSON-Tests-SvenVanCaekenberghe.30

New #allowNil option to Object mapping so that null values are accepted and 
returned as nil when reading a schema using #nextAs: (off by default)

Added NeoJSONObjectMapping>>#allowNil
Added NeoJSONReaderTests>>#testAllowNil
===

From the unit test, this allows you to do as follows:

self
  assert: ((NeoJSONReader on: 'null' readStream)
             mapInstVarsFor: Point;
             for: Point do: [ :mapping | mapping allowNil ];
             nextAs: Point)
  equals: nil.

self
  assert: ((NeoJSONReader on: '[ { "x" : 1, "y" : 2 }, null, { "x" : 3, "y" : 
-1 } ]' readStream)
             mapInstVarsFor: Point;
             for: Point do: [ :mapping | mapping allowNil ];
             for: #ArrayOfPoints customDo: [ :mapping | mapping 
listOfElementSchema: Point ];
             nextAs: #ArrayOfPoints)
  equals: { 1 @ 2. nil. 3 @ -1 }.

I decided to make this a per mapping option, not a per reader option.

> On 23 Dec 2015, at 11:19, Skip Lentz <[email protected]> wrote:
> 
> Hi, thanks for replying! I can share some ideas.
> 
>> On Dec 23, 2015, at 12:09 AM, Sven Van Caekenberghe <[email protected]> wrote:
>> 
>> Hi,
>> 
>> NeoJSON does have a bit of a split personality. 
> 
> Yes, this is what I noticed. Apart from this, I think it’s an awesome 
> library, and I thank you a lot for making it.
> It might be good to not make a distinction between #nextAs: and #next, and 
> only rely on #nextAs:. Then #next might be rewritten as such:
> 
> NeoJSONReader>>next
> 
> ^ self nextAs: self mapClass
> 
> Don’t know if that makes sense or not. It might alleviate this split 
> personality, as you put it.

No, that would not help ;-)

The two approaches are fundamentally different: either you accept anything that 
is present (dynamic mode), returning lists (Arrays) and maps (Dictionaries), or 
you accept a schema that is predefined (static mode).

>> When you use mappings and #nextAs: it tries to map a static type structure 
>> on what is coming in. As such it is not really prepared for dynamic 
>> surprises like objects being replaced by null.
> 
> Yes, in the general case, once you’ve sent #nextAs: to the reader with your 
> value schema, you’re “stuck” with that exact schema.
> 
> While working on the bindings for the GitHub API, there are some JSON 
> representations which send a “type” field (e.g. “type” : “commit”).
> I would like it to be possible to change the class schema to that of a 
> subclass of the original class when detecting a combination of fields.
> 
> That is, I specify a combination of fields, and if any is present, then 
> change the class of the current object to a subclass. By specifying 
> combinations of fields, you don't rely on the order in which they appear in 
> the JSON.
> 
> Maybe in this way it is possible to accomplish without giving up the nice 
> property of NeoJSON not making intermediate structures? Sounds tough to do 
> without turning it into a hack though..

I know what you mean, there are 2 common approaches (some would call it a wrong 
way to use JSON, but that is another discussion):

  { "type":"Point", "x":1, "y":2 }

or

  { "type":"Point", "value": {"x":1, "y":2} }

(Type could be class, or whatever). 

The first case is bad, you have to read everything before you can make a 
decision, which IMO kills the advantage of mapping while trying to avoid 
intermediate structures (i.e. you could do the mapping yourself afterwards).

The second case is a bit better, but not much. Both top level elements could 
have switched place, in which case you already have to read the value, not 
knowing what to map it to.

You could try experimenting with Custom mappings, they allow arbitrary 
conversions, but I doubt if you could really prevent intermediate structure 
creation. But maybe I just don't see it right, and it is possible after all.

If the returned JSON is highly dynamic and variable, I would just use dynamic 
mode and then in a second pass convert the maps/lists to domain objects myself. 
Or someone should write a generic maps/lists to domain model objects mapper 
(independent of JSON).

>> Do also note that the typing info is totally absent from the JSON itself, 
>> unlike STON for example. NeoJSON also tries (and succeeds) in maintaining a 
>> stream based parser that does not generate intermediate structures except 
>> your own domain models.
>> 
>> But your request does make sense and I will think about it. It might be 
>> possible. It has been a while that I really thought about the implementation.
> 
> Alright, I’m willing to help if needed. We can discuss in this e-mail thread.

Thanks for the feedback/discussion.

Regards,

Sven


Reply via email to