NeoJSON to ignore root level

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

NeoJSON to ignore root level

mmimica
Hello

I have obtained a json from a HTTP response which looks like:
{"login":{"verified":true,"authCookie":"2eb62b41-5156-4056-9b5d-9252b52c93a2"}}

I want to map this into a Smalltalk class LoginResponse which has two instance variables: verified and authCookie. So I need the mapper to ignore the root property name "login". I think that is a quite common use case. How can it be done?


--
Milan Mimica
http://sparklet.sf.net
Reply | Threaded
Open this post in threaded view
|

Re: NeoJSON to ignore root level

Sven Van Caekenberghe
Hi Milan,

On 20 Aug 2012, at 14:02, Milan Mimica <[hidden email]> wrote:

> Hello
>
> I have obtained a json from a HTTP response which looks like:
> {"login":{"verified":true,"authCookie":"2eb62b41-5156-4056-9b5d-9252b52c93a2"}}
>
> I want to map this into a Smalltalk class LoginResponse which has two instance variables: verified and authCookie. So I need the mapper to ignore the root property name "login". I think that is a quite common use case. How can it be done?
>
>
> --
> Milan Mimica
> http://sparklet.sf.net

I understand your question, but currently there is no out of the box support for class/type mapping using maps like in your example (there are other variation too, like class/type properties, nested or not). The reason is that this involves schema checking which I didn't want to do due to the added complexity and lack of standardization (this is intentionally outside the JSON spec proper).

You have 2 options:

- either use generic parsing and do the conversions and schema checking after the parsing (which is what most JSON parser make you do anyway)

NeoJSONReader fromString: '{"login":{"verified":true,"authCookie":"2eb62b41-5156-4056-9b5d-9252b52c93a2"}}'.

- write a custom mapping that looks a bit like a hack

(NeoJSONReader on: '{"login":{"verified":true,"authCookie":"2eb62b41-5156-4056-9b5d-9252b52c93a2"}}' readStream)
        for: #LoginResponseWrapper customDo: [ :mapping |
                mapping reader: [ :jsonReader | | result |
                        result := nil.
                        jsonReader parseMapKeysDo: [ :key |
                                key = #login
                                        ifTrue: [ result := jsonReader next ] ].
                        result ] ];
        nextAs: #LoginResponseWrapper

the above example will go into the login element if it is present and return a dictionary.

To make it return your class, this should do the trick

(NeoJSONReader on: '{"login":{"verified":true,"authCookie":"2eb62b41-5156-4056-9b5d-9252b52c93a2"}}' readStream)
        mapInstVarsFor: LoginResponse;
        for: #LoginResponseWrapper customDo: [ :mapping |
                mapping reader: [ :jsonReader | | result |
                        result := nil.
                        jsonReader parseMapKeysDo: [ :key |
                                key = #login
                                        ifTrue: [ result := jsonReader nextAs: LoginResponse ] ].
                        result ] ];
        nextAs: #LoginResponseWrapper

I can image a more general purpose LoginResponseWrapper that maps multiple types at once.

HTH,

Sven



--
Sven Van Caekenberghe
http://stfx.eu
Smalltalk is the Red Pill





Reply | Threaded
Open this post in threaded view
|

Re: NeoJSON to ignore root level

mmimica
That you very much for your input and a great library. I will use a variation of the second solution.


On 20 August 2012 15:34, Sven Van Caekenberghe <[hidden email]> wrote:
Hi Milan,

On 20 Aug 2012, at 14:02, Milan Mimica <[hidden email]> wrote:

> Hello
>
> I have obtained a json from a HTTP response which looks like:
> {"login":{"verified":true,"authCookie":"2eb62b41-5156-4056-9b5d-9252b52c93a2"}}
>
> I want to map this into a Smalltalk class LoginResponse which has two instance variables: verified and authCookie. So I need the mapper to ignore the root property name "login". I think that is a quite common use case. How can it be done?
>
>
> --
> Milan Mimica
> http://sparklet.sf.net

I understand your question, but currently there is no out of the box support for class/type mapping using maps like in your example (there are other variation too, like class/type properties, nested or not). The reason is that this involves schema checking which I didn't want to do due to the added complexity and lack of standardization (this is intentionally outside the JSON spec proper).

You have 2 options:

- either use generic parsing and do the conversions and schema checking after the parsing (which is what most JSON parser make you do anyway)

NeoJSONReader fromString: '{"login":{"verified":true,"authCookie":"2eb62b41-5156-4056-9b5d-9252b52c93a2"}}'.

- write a custom mapping that looks a bit like a hack

(NeoJSONReader on: '{"login":{"verified":true,"authCookie":"2eb62b41-5156-4056-9b5d-9252b52c93a2"}}' readStream)
        for: #LoginResponseWrapper customDo: [ :mapping |
                mapping reader: [ :jsonReader | | result |
                        result := nil.
                        jsonReader parseMapKeysDo: [ :key |
                                key = #login
                                        ifTrue: [ result := jsonReader next ] ].
                        result ] ];
        nextAs: #LoginResponseWrapper

the above example will go into the login element if it is present and return a dictionary.

To make it return your class, this should do the trick

(NeoJSONReader on: '{"login":{"verified":true,"authCookie":"2eb62b41-5156-4056-9b5d-9252b52c93a2"}}' readStream)
        mapInstVarsFor: LoginResponse;
        for: #LoginResponseWrapper customDo: [ :mapping |
                mapping reader: [ :jsonReader | | result |
                        result := nil.
                        jsonReader parseMapKeysDo: [ :key |
                                key = #login
                                        ifTrue: [ result := jsonReader nextAs: LoginResponse ] ].
                        result ] ];
        nextAs: #LoginResponseWrapper

I can image a more general purpose LoginResponseWrapper that maps multiple types at once.

HTH,

Sven



--
Sven Van Caekenberghe
http://stfx.eu
Smalltalk is the Red Pill








--
Milan Mimica
http://sparklet.sf.net