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 <skip.lentz@inria.fr> wrote:
Hi, thanks for replying! I can share some ideas.
On Dec 23, 2015, at 12:09 AM, Sven Van Caekenberghe <sven@stfx.eu> 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