Understanding ZnJSONReader mapping?

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

Understanding ZnJSONReader mapping?

Tim Mackinnon
Hi - is there something I can read/study to get a bit more familiar with Json mapping? I have read the Pharo enterprise book (chapter 8).

I’ve been using NeoJSONObject but then it occurred to me that maybe I could map better domain objects directly and simplify things. However it seems that the intent of mapping is just for simple things like an array of points - whereas if I have 15 different domain objects (albeit with a type key), then I’m wondering if I should just wrap json nodes on usage?

In fact I’m wondering what the point of mapping is if it’s so simple - or maybe I’m missing an important concept?

Tim

Sent from my iPhone

Reply | Threaded
Open this post in threaded view
|

Re: Understanding ZnJSONReader mapping?

Esteban A. Maringolo
On 07/06/2018 17:21, Tim Mackinnon wrote:
> Hi - is there something I can read/study to get a bit more familiar with Json mapping? I have read the Pharo enterprise book (chapter 8).
>
> I’ve been using NeoJSONObject but then it occurred to me that maybe I could map better domain objects directly and simplify things. However it seems that the intent of mapping is just for simple things like an array of points - whereas if I have 15 different domain objects (albeit with a type key), then I’m wondering if I should just wrap json nodes on usage?
>
> In fact I’m wondering what the point of mapping is if it’s so simple - or maybe I’m missing an important concept?
If your objects just hold data, then you can get very far with
NeoJSONObject, but if you want to do more, you can get pretty extensible
mappings with the use of the valueSchema: option in the mapper.

For instance, to write a TsOrder (a model of an purchase order) I
configured the mapper this way:

mapper for: TsOrder do: [ :mapping |
  mapping mapAccessors: #(id).
 (mapping mapAccessor: #date) valueSchema: Date.
 (mapping mapAccessor: #edd) valueSchema: #NamedModel.
 (mapping mapAccessor: #provider) valueSchema: #NamedModel.
 (mapping mapAccessor: #status) valueSchema: #NamedModel.
 (mapping mapAccessor: #customerCode).
 (mapping mapAccessor: #totalOriginalPrice to: 'total') valueSchema:
#monetary.
].

mapper for: Date customDo: [ :mapping |
    mapping encoder: [ :date | date ddmmyyyy  ] ].

mapper for: #monetary customDo: [ :mapping |
    mapping encoder: [ :price | price printShowingDecimalPlaces: 2 ] ].

mapper for: DateAndTime customDo: [ :mapping |
    mapping decoder: [ :string | DateAndTime fromString: string ].
 ].

mapper for: #NamedModel customDo: [ :mapping |
    mapping encoder: [ :persona | persona displayString ] ].


For decoding it might be trickier, because you might need more objects
other than just deserializing from JSON, e.g. to avoid instantiating
twice the same domain object, or if you need to lookup an entity in the
database by ID, and then you need access to the database, etc.

Throughout my journey of developing a few JSON-REST APIs I started using
the NeoJSON approach, then JsonObject and then WAJsonCanvas.

I found myself more confortable writing JSON with the latter (I like the
canvas approach of Seaside) and instantiating NeoJSONObject/JsonObject
and building my business objects from these cherry picking what I needed.

What NeoJSON offers as a "differentiator" is that it is stream based, so
there is no need to have intermediate "structures" (it is,
dictionaries), and the mappers are orthogonal to the objects (unless you
use the default class side #neoJsonMapping:)

WAJsonCanvas offers stream based writing, but there are no mappings, but
if you always serialize a proper hierarchy of renderJsonOn: implementors
could prove to be effective, in particular if you have to build your
objects manually from a JSON stream.

Regards,


--
Esteban A. Maringolo

Reply | Threaded
Open this post in threaded view
|

Re: Understanding ZnJSONReader mapping?

Tim Mackinnon
Hey thanks - so maybe there are more legs to this - although it’s the decoding I’m currently interested in (but I’m sure encoding will come up at some point on my journey).

So it seems that my mapper can have multiple entries (that bit I had missed - although seems obvious in retrospect now).

But then you have have to say #nextAs: and this is what I don’t get for anything more complicated - how do I choose as it could be any one of the patterns I want to map?

Sadly it’s not my scheme - it’s a prismic.io one so I’m trying to follow what the equivalent python/js/csharp libs do - but they are horrid, filled with tons on instanceof crap...

I’ve gotten reasonably far but as the structures can be recursive for a cms, I think I can do much better somehow.

Tim

Sent from my iPhone

> On 7 Jun 2018, at 21:44, Esteban A. Maringolo <[hidden email]> wrote:
>
>> On 07/06/2018 17:21, Tim Mackinnon wrote:
>> Hi - is there something I can read/study to get a bit more familiar with Json mapping? I have read the Pharo enterprise book (chapter 8).
>>
>> I’ve been using NeoJSONObject but then it occurred to me that maybe I could map better domain objects directly and simplify things. However it seems that the intent of mapping is just for simple things like an array of points - whereas if I have 15 different domain objects (albeit with a type key), then I’m wondering if I should just wrap json nodes on usage?
>>
>> In fact I’m wondering what the point of mapping is if it’s so simple - or maybe I’m missing an important concept?
> If your objects just hold data, then you can get very far with
> NeoJSONObject, but if you want to do more, you can get pretty extensible
> mappings with the use of the valueSchema: option in the mapper.
>
> For instance, to write a TsOrder (a model of an purchase order) I
> configured the mapper this way:
>
> mapper for: TsOrder do: [ :mapping |
>  mapping mapAccessors: #(id).
> (mapping mapAccessor: #date) valueSchema: Date.
> (mapping mapAccessor: #edd) valueSchema: #NamedModel.
> (mapping mapAccessor: #provider) valueSchema: #NamedModel.
> (mapping mapAccessor: #status) valueSchema: #NamedModel.
> (mapping mapAccessor: #customerCode).
> (mapping mapAccessor: #totalOriginalPrice to: 'total') valueSchema:
> #monetary.
> ].
>
> mapper for: Date customDo: [ :mapping |
>    mapping encoder: [ :date | date ddmmyyyy  ] ].
>
> mapper for: #monetary customDo: [ :mapping |
>    mapping encoder: [ :price | price printShowingDecimalPlaces: 2 ] ].
>
> mapper for: DateAndTime customDo: [ :mapping |
>    mapping decoder: [ :string | DateAndTime fromString: string ].
> ].
>
> mapper for: #NamedModel customDo: [ :mapping |
>    mapping encoder: [ :persona | persona displayString ] ].
>
>
> For decoding it might be trickier, because you might need more objects
> other than just deserializing from JSON, e.g. to avoid instantiating
> twice the same domain object, or if you need to lookup an entity in the
> database by ID, and then you need access to the database, etc.
>
> Throughout my journey of developing a few JSON-REST APIs I started using
> the NeoJSON approach, then JsonObject and then WAJsonCanvas.
>
> I found myself more confortable writing JSON with the latter (I like the
> canvas approach of Seaside) and instantiating NeoJSONObject/JsonObject
> and building my business objects from these cherry picking what I needed.
>
> What NeoJSON offers as a "differentiator" is that it is stream based, so
> there is no need to have intermediate "structures" (it is,
> dictionaries), and the mappers are orthogonal to the objects (unless you
> use the default class side #neoJsonMapping:)
>
> WAJsonCanvas offers stream based writing, but there are no mappings, but
> if you always serialize a proper hierarchy of renderJsonOn: implementors
> could prove to be effective, in particular if you have to build your
> objects manually from a JSON stream.
>
> Regards,
>
>
> --
> Esteban A. Maringolo
>


Reply | Threaded
Open this post in threaded view
|

Re: Understanding ZnJSONReader mapping?

Tim Mackinnon
Its sort of an awkward format, so maybe mapping is just not the way to go (but I’m still keen to learn how to properly use it when it is appropriate). For the record (if anyone is interested), an example json sample is here (https://sprintdemo.prismic.io/api/v1/documents/search?q=%5B%5Bat(document.tags,%20%5B%22demo%22%5D)%5D%5D&ref=WxmMWCQAAFGEIbhe#format=json).

It ends up giving you something like below (the names like heading, effectivefrom are names you define in your Prismic page type, but their values are then a standard format (almost) - so maybe those I can usefully map?

Or maybe I just stick to dictionaries and plough on.

Tim

page: {
  • -
    -
    heading: {
    • type"StructuredText"
    • -
      -
      value: [
      • -
        -
        {
        • type"heading1"
        • text"Sample Page"
        • spans: [ ]
        }
      ]
    }

  • -
    -
    effectivefrom: {
    • type"Timestamp"
    • value"2018-06-20T23:00:00+0000"
    }
  • -
    -
    department: {
    • type"Select"
    • value“SMA"
    • }
-
 - content: {
  • type"StructuredText"
  • -
    -
    value: [
    • -
      -
      {
      • type"paragraph"
      • text"This is a sample page with some content."
      • spans: [ ]
      }
    • -
      +
      { … }
    • -
      -
      {
      • type"paragraph"
      • text"There are 4 rules:"
      • -
        -
        spans: [
        • -
          -
          {
          • start10
          • end17
          • type"strong"
          }
        ]
      }


On 7 Jun 2018, at 21:58, Tim Mackinnon <[hidden email]> wrote:

Hey thanks - so maybe there are more legs to this - although it’s the decoding I’m currently interested in (but I’m sure encoding will come up at some point on my journey).

So it seems that my mapper can have multiple entries (that bit I had missed - although seems obvious in retrospect now).

But then you have have to say #nextAs: and this is what I don’t get for anything more complicated - how do I choose as it could be any one of the patterns I want to map?

Sadly it’s not my scheme - it’s a prismic.io one so I’m trying to follow what the equivalent python/js/csharp libs do - but they are horrid, filled with tons on instanceof crap...

I’ve gotten reasonably far but as the structures can be recursive for a cms, I think I can do much better somehow.

Tim

Sent from my iPhone

On 7 Jun 2018, at 21:44, Esteban A. Maringolo <[hidden email]> wrote:

On 07/06/2018 17:21, Tim Mackinnon wrote:
Hi - is there something I can read/study to get a bit more familiar with Json mapping? I have read the Pharo enterprise book (chapter 8).

I’ve been using NeoJSONObject but then it occurred to me that maybe I could map better domain objects directly and simplify things. However it seems that the intent of mapping is just for simple things like an array of points - whereas if I have 15 different domain objects (albeit with a type key), then I’m wondering if I should just wrap json nodes on usage?

In fact I’m wondering what the point of mapping is if it’s so simple - or maybe I’m missing an important concept?
If your objects just hold data, then you can get very far with
NeoJSONObject, but if you want to do more, you can get pretty extensible
mappings with the use of the valueSchema: option in the mapper.

For instance, to write a TsOrder (a model of an purchase order) I
configured the mapper this way:

mapper for: TsOrder do: [ :mapping |
mapping mapAccessors: #(id).
(mapping mapAccessor: #date) valueSchema: Date.
(mapping mapAccessor: #edd) valueSchema: #NamedModel.
(mapping mapAccessor: #provider) valueSchema: #NamedModel.
(mapping mapAccessor: #status) valueSchema: #NamedModel.
(mapping mapAccessor: #customerCode).
(mapping mapAccessor: #totalOriginalPrice to: 'total') valueSchema:
#monetary.
].

mapper for: Date customDo: [ :mapping |
  mapping encoder: [ :date | date ddmmyyyy  ] ].

mapper for: #monetary customDo: [ :mapping |
  mapping encoder: [ :price | price printShowingDecimalPlaces: 2 ] ].

mapper for: DateAndTime customDo: [ :mapping |
  mapping decoder: [ :string | DateAndTime fromString: string ].
].

mapper for: #NamedModel customDo: [ :mapping |
  mapping encoder: [ :persona | persona displayString ] ].


For decoding it might be trickier, because you might need more objects
other than just deserializing from JSON, e.g. to avoid instantiating
twice the same domain object, or if you need to lookup an entity in the
database by ID, and then you need access to the database, etc.

Throughout my journey of developing a few JSON-REST APIs I started using
the NeoJSON approach, then JsonObject and then WAJsonCanvas.

I found myself more confortable writing JSON with the latter (I like the
canvas approach of Seaside) and instantiating NeoJSONObject/JsonObject
and building my business objects from these cherry picking what I needed.

What NeoJSON offers as a "differentiator" is that it is stream based, so
there is no need to have intermediate "structures" (it is,
dictionaries), and the mappers are orthogonal to the objects (unless you
use the default class side #neoJsonMapping:)

WAJsonCanvas offers stream based writing, but there are no mappings, but
if you always serialize a proper hierarchy of renderJsonOn: implementors
could prove to be effective, in particular if you have to build your
objects manually from a JSON stream.

Regards,


--
Esteban A. Maringolo