[Pharo-users] Embedding object properties in NeoJSON mappings

Sven Van Caekenberghe sven at stfx.eu
Fri Feb 22 05:54:23 EST 2019


> On 22 Feb 2019, at 02:05, Esteban Maringolo <emaringolo at gmail.com> wrote:
> Hi all (again),
> As part of writing the port (and adaptation to use Zinc and NeoJSON),
> a CouchDB JSON-REST API, now I find something I don't know how to
> solve using NeoJSON mappings.
> All objects you store in CouchDB at a particular URI are saved as
> "Documents", so if you save a dictionary with keys "name", "height",
> "c" to  "/people/1234" , when you query "people/1234" you get the keys
> of the saved dictionary PLUS to extra keys called _id and _rev.
> Then when you query (GET) back the document you get a JSON like this:
> {"_id": ..., "_rev": ..., "name": ..., "height":...,}
> My Idea is that I can wrap any object serializable as JSON within
> aDocument, but then if I define CouchDocument class>>#neoJsonMapping:
> mapper as:
> mapper for: self do: [ :mapping |
>  mapping mapAccessor: #id to: '_id'.
>  mapping mapAccessor: #revision to: '_rev'.
>  mapping mapAccessor: #class to: '_class'
> ].
> I have no way to "flatten"/"merge" anObject within aDocument.
> I would expect something like
>  mapper for: self do: [ :mapping |
>    mapping mapAccessor: #id to: '_id'.
>    mapping mapAccessor: #revision to: '_rev'.
>    mapping mapAccessor: #class to: '_class'
>    mapping embed: [:object | object whatToEmbed]
> ].
> Of course this is awkward, because if I need to read it back, the
> "extraction"  of the embeded properties needs to know which belongs to
> which and more importantly , what to instantiate!
> Of course I can do it manually without using NeoJSON (It's already
> being done [1]) , but I like when different libraries things fit
> naturally together :)
> Am I stretching it too much? :)

Yes, this is out of the scope of NeoJSON.

As you know, JSON is strictly limited and kept simple, by design. Things people do to shoehorn objects, classes, types, ids, collections, references, etc on to it are exactly that, hacks on top. There are many ways to try to do any of this.

What you are doing now, a two pass approach, is what I would do too: first use NeoJSON to get a standard structure (maybe use NeoJSONObject for simplicity), next do some extra work - and vice versa.

Note that Pharo itself might lack some meta info to describe a mapping by itself (collections have no element types for example), so a good mapping is always necessary to go to a statically typed model.

There is a reason STON exists, it is one answer to this problem.


> Regards,
> [1] Currently the writing is done manually and the refresh (which is
> also a GET) does the following:
> CouchDocument>>#refresh
> " Update this Document to the latest revision "
> | response |
> response := self information. "response is a Dictionary"
> revision := response at: '_rev'.
> object := response couchToObject.
> ^response
> And here happens the magic, it searchs for a "_class" key which if
> present will use to instantiate the object "wrapped" by the document.
> Dictionary>>couchToObject
> | class object |
> self removeKey: '_id' ifAbsent: [].
> self removeKey: '_rev' ifAbsent: [].
> self keysAndValuesDo: [:key :value | self at: key put: value couchToObject].
> class := (self at: '_class' ifAbsent: [^self]) asSymbol asClass.
> object := class basicNew.
> object jsonInstanceVariableNames keysAndValuesDo: [:index :key |
>   self at: key ifPresent: [:value | object instVarAt: index put: value]].
> ^object
> #jsonInstanceVariableNames is another level of "mapping" here, that
> defined what to map from the Document response.
> Esteban A. Maringolo

More information about the Pharo-users mailing list