NeoJSON mapping question

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

NeoJSON mapping question

BrunoBB
Hi,

How to set a path to JSON value in the mapping definition ?

I mean i have a simple object with and instance variable but the value of
this inst var is deep inside the JSON.

mapping mapInstVar: #locator to: 'locator'.

This fail because 'locator' is not in the first JSON level.

Is possible to do something like:
mapping mapInstVar: #locator to: 'booking.vars.locator'.

I already tried and fail. But i was not able to find something similar.

regards,
bruno



--
Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html

Reply | Threaded
Open this post in threaded view
|

Re: NeoJSON mapping question

BrunoBB
Hi,

The following implementation do what i want without the need to create a
class for each level of the JSON, however i really dislike my solution.
neoJsonMapping: mapper "class side"
        "Map the receiver to a json with <NeoJSONObjectMapping> <mapper>"
        mapper for: self do: [ :mapping |
                (mapping mapProperty: 'booking' getter: [:obj | obj locator] setter: [
:obj :value | obj setLocator: value]) valueSchema: self.
        ].

  mapper for: self do: [ :mapping |
                mapping mapProperty: '@locator' getter: [:obj | obj locator] setter: [
:obj :value | obj locator: value]
        ]

The first part map 'booking' property to the class locator inst var. The
second part have access to the "second json level" and the value i'm looking
for.

But setLocator: anObject is the ugly thing :(
setLocator: anObject
        (anObject class == PmsJsonInput) ifTrue: [^locator := anObject locator].
        locator := anObject

I'm sure there must be another way to do this.

regards,
bruno



--
Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html

Reply | Threaded
Open this post in threaded view
|

Re: NeoJSON mapping question

BrunoBB
Hi,

Finally i found a much more acceptable solution :)

neoJsonMapping: mapper
        "Map the receiver from a json with <NeoJSONObjectMapping> <mapper>"
        mapper for: self do: [ :mapping |
                mapping mapAccessor: #locator mutator: #setLocator: to: 'booking'.
        ].

setLocator: aDictionary
        locator := aDictionary at: '@locator'

regards,
bruno



--
Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html

Reply | Threaded
Open this post in threaded view
|

Re: NeoJSON mapping question

Sven Van Caekenberghe-2
In reply to this post by BrunoBB
Bruno,

What about the following ?

json := '{ "id" : 1, "level1" : { "level2" : { "name" : "foo" } } }'.

In the JSON above there is a nesting level1.level2.name that you want to skip over.

One solution would be:

NeoJSONReader new
  on: json readStream;
  for: #Foo customDo: [ :mapping |
    mapping decoder: [ :x |
          (x at: #id) -> (((x at: #level1) at: #level2) at: #name) ] ];
  nextAs: #Foo.

The returned type is an Association, but it could be a domain object. #Foo is just an identifier. The disadvantage is that anything deeper is not mapped, but comes in as a plain dictionary, in a sense defeating the concept of mapping (but for leaf like structures this might be what you want).

Alternatively, if you use NeoJSONObject instead of Dictionary, it becomes easier to access a nested path:

NeoJSONReader new
  on: json readStream;
  mapClass: NeoJSONObject;
  for: #Foo customDo: [ :mapping |
    mapping decoder: [ :x |
          (x at: #id) -> (x atPath: #(level1 level2 name)) ] ];
  nextAs: #Foo.

HTH,

Sven

PS: Note also that mappings can live on the class side (where they are global), but also inside specific NeoJSONReader instances (where they are local), like here.

> On 26 Feb 2019, at 03:50, BrunoBB <[hidden email]> wrote:
>
> Hi,
>
> The following implementation do what i want without the need to create a
> class for each level of the JSON, however i really dislike my solution.
> neoJsonMapping: mapper "class side"
> "Map the receiver to a json with <NeoJSONObjectMapping> <mapper>"
> mapper for: self do: [ :mapping |
> (mapping mapProperty: 'booking' getter: [:obj | obj locator] setter: [
> :obj :value | obj setLocator: value]) valueSchema: self.
> ].
>
> mapper for: self do: [ :mapping |
> mapping mapProperty: '@locator' getter: [:obj | obj locator] setter: [
> :obj :value | obj locator: value]
> ]
>
> The first part map 'booking' property to the class locator inst var. The
> second part have access to the "second json level" and the value i'm looking
> for.
>
> But setLocator: anObject is the ugly thing :(
> setLocator: anObject
> (anObject class == PmsJsonInput) ifTrue: [^locator := anObject locator].
> locator := anObject
>
> I'm sure there must be another way to do this.
>
> regards,
> bruno
>
>
>
> --
> Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
>


Reply | Threaded
Open this post in threaded view
|

Re: NeoJSON mapping question

Esteban A. Maringolo
El mar., 26 feb. 2019 a las 17:57, Sven Van Caekenberghe
(<[hidden email]>) escribió:

> PS: Note also that mappings can live on the class side (where they are global), but also inside specific NeoJSONReader instances (where they are local), like here.

This postscript should be a whole section in the docs, I remember it
took me some time to fully understand this, and how flexible this
could be.

Also I found confusing the semantics of the selectors and variable for
mapping/mapper, because it is the `mapping` that adds a new mapping
(e.g. #mapAccessor:).
But this might be just me.

Regards!

Reply | Threaded
Open this post in threaded view
|

Re: NeoJSON mapping question

Sven Van Caekenberghe-2


> On 26 Feb 2019, at 22:23, Esteban Maringolo <[hidden email]> wrote:
>
> El mar., 26 feb. 2019 a las 17:57, Sven Van Caekenberghe
> (<[hidden email]>) escribió:
>
>> PS: Note also that mappings can live on the class side (where they are global), but also inside specific NeoJSONReader instances (where they are local), like here.
>
> This postscript should be a whole section in the docs, I remember it
> took me some time to fully understand this, and how flexible this
> could be.
>
> Also I found confusing the semantics of the selectors and variable for
> mapping/mapper, because it is the `mapping` that adds a new mapping
> (e.g. #mapAccessor:).
> But this might be just me.

Well, terminology is hard, to come up with, to communicate, etc. I certainly don't think that the situation is perfect.

Both a reader and a writer are a mapper, because they (might) need to convert to/from types called schemas. (They might not need it if they only use Arrays and Dictionaries).

A schema is either a real class name, or just an abstract name to refer to (that last use is more for reading, like ArrayOfPoints, because the concept of a typed collection does not exist in Pharo, although there is also #nextPut:as:).

When you map a schema, you describe how it is read/written. You do that either with a very general block (custom mapping) or according to the common Pharo model (object mapping: a Pharo object/class with named properties).

In that last case, there is indeed a second level of mapping: that of the properties making up the object/class. Here you find mapAccessors, mapInstVars, etc (see the protocol mapping & convenience of NeoJSONObjectMapping).

A key issue is that (especially when reading) schema/type information must be passed along (again because the concept of type of an instance variable does not exist in Pharo). Especially collections are hard to map.

Some JSON is very dynamic in nature and does not follow a strict static structure of types.

> Regards!


Reply | Threaded
Open this post in threaded view
|

Re: NeoJSON mapping question

BrunoBB
Hi Sven,

Excellent !!! That exactly what i want.
As an excuse i will say i'm one day old with NeoJSON :)

mapping decoder: [:x | ] plus #atPath: made my day !

Thanks very much for you clear response.

regards,
bruno



--
Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html