[Pharo-users] Embedding object properties in NeoJSON mappings

Esteban Maringolo emaringolo at gmail.com
Thu Feb 21 20:05:30 EST 2019

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? :)


[1] Currently the writing is done manually and the refresh (which is
also a GET) does the following:

" Update this Document to the latest revision "
| response |
response := self information. "response is a Dictionary"
revision := response at: '_rev'.
object := response couchToObject.

And here happens the magic, it searchs for a "_class" key which if
present will use to instantiate the object "wrapped" by the document.

| 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]].

#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