NeoJSON Custom Mapping Question

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

NeoJSON Custom Mapping Question

darth-cheney
Hey guys,

I'm toying with the idea of a Pharo implementation of ActivityStreams (https://www.w3.org/TR/activitystreams-core/#introduction) which is based on a subset of JSON called JSON-LD (for "linked data").

While I do not believe this would entail creating a whole JSON-LD implementation, I do have a question about how to implement reading/writing/mapping to and from the core set of object types in the specification.

I am aware that giving any class #neoJsonMapping allows one to define the specifics of the mapping to and from objects of that class (I think this is super cool, by the way). The issue with ActivityStreams objects is that the "type" of object they represent is in the JSON itself as the property "type," for example:

```
{
"@context": "https://www.w3.org/ns/activitystreams",
"name": "A simple note",
"type": "Note",
"id": "http://www.test.example/notes/1",
"content": "A simple note",
"replies": {
     "type": "Collection",
     "totalItems": 1,
     "items": [
         {
             "name": "A response to the note",
             "type": "Note",
             "content": "A response to the note",
             "inReplyTo": "http://www.test.example/notes/1"
         }
     ]
}
}
```

This refers to a top level ActivityStream object of type "Note" which we maybe want to parse into something like `ASNote`.

My core question is this: how can we get a NeoJSONReader to determine which type of object to create by checking for a "type" property as soon as possible in the process? Because this property can show up in any order, we won't know what type of object to create until it is encountered. What is the best strategy there?

Does the complexity suggest that we should simply parse incoming ActivityStream objects into the collection-and-dict default mapping and deal with creating the appropriate objects after that? In this case we could still use the custom mapping for the writing, which is not a problem since the "type" field will be associated with the class (ie, ASNote class >> #type will respond with 'Note' and we can go from there).

Any thoughts or suggestions much appreciated!
--
Eric
Reply | Threaded
Open this post in threaded view
|

Re: NeoJSON Custom Mapping Question

Esteban A. Maringolo
This could be very useful, in the past I used ExtJS which serialized its components in JSON format with a similar "xtype" attribute that was used to lookup the proper class to instantiate.

But NeoJSON being stream based, and requiring a mapping to instantiate specific classes, the only, dirty, way I think about it is to instantiate everything as NeoJSONObject and then instantiate your desired object classes from the tree/graph of NeoJSONObjects.

So basically you'd have to read the whole JSON stream and *then* instantiate your objects, this is more memory intensive than a stream based approach because you'll be instantiating intermediary objects, but it could work fine.

Something like:

ASNote fromJSON:  
(NeoJSONReader on: readStream)
mapClass: NeoJSONObject;
upToEnd


Regards,


Esteban A. Maringolo


El mar., 8 ene. 2019 a las 20:35, Eric Gade (<[hidden email]>) escribió:
Hey guys,

I'm toying with the idea of a Pharo implementation of ActivityStreams (https://www.w3.org/TR/activitystreams-core/#introduction) which is based on a subset of JSON called JSON-LD (for "linked data").

While I do not believe this would entail creating a whole JSON-LD implementation, I do have a question about how to implement reading/writing/mapping to and from the core set of object types in the specification.

I am aware that giving any class #neoJsonMapping allows one to define the specifics of the mapping to and from objects of that class (I think this is super cool, by the way). The issue with ActivityStreams objects is that the "type" of object they represent is in the JSON itself as the property "type," for example:

```
{
"@context": "https://www.w3.org/ns/activitystreams",
"name": "A simple note",
"type": "Note",
"id": "http://www.test.example/notes/1",
"content": "A simple note",
"replies": {
     "type": "Collection",
     "totalItems": 1,
     "items": [
         {
             "name": "A response to the note",
             "type": "Note",
             "content": "A response to the note",
             "inReplyTo": "http://www.test.example/notes/1"
         }
     ]
}
}
```

This refers to a top level ActivityStream object of type "Note" which we maybe want to parse into something like `ASNote`.

My core question is this: how can we get a NeoJSONReader to determine which type of object to create by checking for a "type" property as soon as possible in the process? Because this property can show up in any order, we won't know what type of object to create until it is encountered. What is the best strategy there?

Does the complexity suggest that we should simply parse incoming ActivityStream objects into the collection-and-dict default mapping and deal with creating the appropriate objects after that? In this case we could still use the custom mapping for the writing, which is not a problem since the "type" field will be associated with the class (ie, ASNote class >> #type will respond with 'Note' and we can go from there).

Any thoughts or suggestions much appreciated!
--
Eric
Reply | Threaded
Open this post in threaded view
|

Re: NeoJSON Custom Mapping Question

Sven Van Caekenberghe-2
In reply to this post by darth-cheney
Eric,

> On 9 Jan 2019, at 00:34, Eric Gade <[hidden email]> wrote:
>
> Hey guys,
>
> I'm toying with the idea of a Pharo implementation of ActivityStreams (https://www.w3.org/TR/activitystreams-core/#introduction) which is based on a subset of JSON called JSON-LD (for "linked data").
>
> While I do not believe this would entail creating a whole JSON-LD implementation, I do have a question about how to implement reading/writing/mapping to and from the core set of object types in the specification.
>
> I am aware that giving any class #neoJsonMapping allows one to define the specifics of the mapping to and from objects of that class (I think this is super cool, by the way). The issue with ActivityStreams objects is that the "type" of object they represent is in the JSON itself as the property "type," for example:
>
> ```
> {
> "@context": "https://www.w3.org/ns/activitystreams",
> "name": "A simple note",
> "type": "Note",
> "id": "http://www.test.example/notes/1",
> "content": "A simple note",
> "replies": {
>      "type": "Collection",
>      "totalItems": 1,
>      "items": [
>          {
>              "name": "A response to the note",
>              "type": "Note",
>              "content": "A response to the note",
>              "inReplyTo": "http://www.test.example/notes/1"
>          }
>      ]
> }
> }
> ```
>
> This refers to a top level ActivityStream object of type "Note" which we maybe want to parse into something like `ASNote`.
>
> My core question is this: how can we get a NeoJSONReader to determine which type of object to create by checking for a "type" property as soon as possible in the process? Because this property can show up in any order, we won't know what type of object to create until it is encountered. What is the best strategy there?
>
> Does the complexity suggest that we should simply parse incoming ActivityStream objects into the collection-and-dict default mapping and deal with creating the appropriate objects after that? In this case we could still use the custom mapping for the writing, which is not a problem since the "type" field will be associated with the class (ie, ASNote class >> #type will respond with 'Note' and we can go from there).
>
> Any thoughts or suggestions much appreciated!

You describe the situation correctly: NeoJSON operates in 2 modes:

(1) generating generic Arrays and Dictionaries (just like the spec)

or

(2) optionally mapping, stream based, directly, without intermediary representation, into domain instances (given a predefined static mapping)

Using NeoJSONObject instead of Dictionary is just a convenience (it being a more JS like object).

There is indeed another approach, using dynamic typing, that is not specifically supported in NeoJSON.

As you remark yourself, that requires an intermediary representation, so one could just as well use option (1) and convert 'manually' in a second pass.

That being said, it could/would be a nice addition to offer some support for this. Although there are multiple slightly different approaches. For example, there also is

{
  "Point" : { "x" : 1, "y" : 2 }
}

Instead of

{
  "_type" : "Point",
  "x" : 1,
  "y" : 2
}

The key for the type/class can be different, the value can be anything, even 'com.java.graphics.Point'.

Note also that typically, there are no collection types (and these would be way more complicated).

Sven

> --
> Eric


Reply | Threaded
Open this post in threaded view
|

Re: NeoJSON Custom Mapping Question

darth-cheney
Hi Sven, thanks for the response.

Unfortunately since I'm working with a standard here, there is always going to be an explicit `"type": <foo>` attribute rather than having the key itself be the name of the type. I guess I'll stick to the two-pass read solution. But the writing solution can use the mapping so that saves a lot of trouble!
 

On Wed, Jan 9, 2019 at 3:14 PM Sven Van Caekenberghe <[hidden email]> wrote:
Eric,

> On 9 Jan 2019, at 00:34, Eric Gade <[hidden email]> wrote:
>
> Hey guys,
>
> I'm toying with the idea of a Pharo implementation of ActivityStreams (https://www.w3.org/TR/activitystreams-core/#introduction) which is based on a subset of JSON called JSON-LD (for "linked data").
>
> While I do not believe this would entail creating a whole JSON-LD implementation, I do have a question about how to implement reading/writing/mapping to and from the core set of object types in the specification.
>
> I am aware that giving any class #neoJsonMapping allows one to define the specifics of the mapping to and from objects of that class (I think this is super cool, by the way). The issue with ActivityStreams objects is that the "type" of object they represent is in the JSON itself as the property "type," for example:
>
> ```
> {
> "@context": "https://www.w3.org/ns/activitystreams",
> "name": "A simple note",
> "type": "Note",
> "id": "http://www.test.example/notes/1",
> "content": "A simple note",
> "replies": {
>      "type": "Collection",
>      "totalItems": 1,
>      "items": [
>          {
>              "name": "A response to the note",
>              "type": "Note",
>              "content": "A response to the note",
>              "inReplyTo": "http://www.test.example/notes/1"
>          }
>      ]
> }
> }
> ```
>
> This refers to a top level ActivityStream object of type "Note" which we maybe want to parse into something like `ASNote`.
>
> My core question is this: how can we get a NeoJSONReader to determine which type of object to create by checking for a "type" property as soon as possible in the process? Because this property can show up in any order, we won't know what type of object to create until it is encountered. What is the best strategy there?
>
> Does the complexity suggest that we should simply parse incoming ActivityStream objects into the collection-and-dict default mapping and deal with creating the appropriate objects after that? In this case we could still use the custom mapping for the writing, which is not a problem since the "type" field will be associated with the class (ie, ASNote class >> #type will respond with 'Note' and we can go from there).
>
> Any thoughts or suggestions much appreciated!

You describe the situation correctly: NeoJSON operates in 2 modes:

(1) generating generic Arrays and Dictionaries (just like the spec)

or

(2) optionally mapping, stream based, directly, without intermediary representation, into domain instances (given a predefined static mapping)

Using NeoJSONObject instead of Dictionary is just a convenience (it being a more JS like object).

There is indeed another approach, using dynamic typing, that is not specifically supported in NeoJSON.

As you remark yourself, that requires an intermediary representation, so one could just as well use option (1) and convert 'manually' in a second pass.

That being said, it could/would be a nice addition to offer some support for this. Although there are multiple slightly different approaches. For example, there also is

{
  "Point" : { "x" : 1, "y" : 2 }
}

Instead of

{
  "_type" : "Point",
  "x" : 1,
  "y" : 2
}

The key for the type/class can be different, the value can be anything, even 'com.java.graphics.Point'.

Note also that typically, there are no collection types (and these would be way more complicated).

Sven

> --
> Eric




--
Eric
Reply | Threaded
Open this post in threaded view
|

Re: NeoJSON Custom Mapping Question

Pharo Smalltalk Users mailing list
Hi Eric,

I‘m late to the game but I‘m interested to talk abd cooperate. I implemented JSON schema  [1] not long ago. This can also be used to add a mapping from object to json. It has also a type key inside the json object. I did it with an intermediate object. The JSONSchemaDefinition is a super type of all JSON schema types meaning one json object containing all keys of all types. I read the super type and then with a visitor
I explode this super type into concrete type objects. As you say on writing I use the concrete objects. The asymmetry is also a bit tricky but in the end the best approach so far. So if you like we can talk. I think that it could be a good idea to think about how JSON Schema and JSON LD could fit together.

Norbert 



Am 09.01.2019 um 22:27 schrieb Eric Gade <[hidden email]>:

Hi Sven, thanks for the response.

Unfortunately since I'm working with a standard here, there is always going to be an explicit `"type": <foo>` attribute rather than having the key itself be the name of the type. I guess I'll stick to the two-pass read solution. But the writing solution can use the mapping so that saves a lot of trouble!
 

On Wed, Jan 9, 2019 at 3:14 PM Sven Van Caekenberghe <[hidden email]> wrote:
Eric,

> On 9 Jan 2019, at 00:34, Eric Gade <[hidden email]> wrote:
>
> Hey guys,
>
> I'm toying with the idea of a Pharo implementation of ActivityStreams (https://www.w3.org/TR/activitystreams-core/#introduction) which is based on a subset of JSON called JSON-LD (for "linked data").
>
> While I do not believe this would entail creating a whole JSON-LD implementation, I do have a question about how to implement reading/writing/mapping to and from the core set of object types in the specification.
>
> I am aware that giving any class #neoJsonMapping allows one to define the specifics of the mapping to and from objects of that class (I think this is super cool, by the way). The issue with ActivityStreams objects is that the "type" of object they represent is in the JSON itself as the property "type," for example:
>
> ```
> {
> "@context": "https://www.w3.org/ns/activitystreams",
> "name": "A simple note",
> "type": "Note",
> "id": "http://www.test.example/notes/1",
> "content": "A simple note",
> "replies": {
>      "type": "Collection",
>      "totalItems": 1,
>      "items": [
>          {
>              "name": "A response to the note",
>              "type": "Note",
>              "content": "A response to the note",
>              "inReplyTo": "http://www.test.example/notes/1"
>          }
>      ]
> }
> }
> ```
>
> This refers to a top level ActivityStream object of type "Note" which we maybe want to parse into something like `ASNote`.
>
> My core question is this: how can we get a NeoJSONReader to determine which type of object to create by checking for a "type" property as soon as possible in the process? Because this property can show up in any order, we won't know what type of object to create until it is encountered. What is the best strategy there?
>
> Does the complexity suggest that we should simply parse incoming ActivityStream objects into the collection-and-dict default mapping and deal with creating the appropriate objects after that? In this case we could still use the custom mapping for the writing, which is not a problem since the "type" field will be associated with the class (ie, ASNote class >> #type will respond with 'Note' and we can go from there).
>
> Any thoughts or suggestions much appreciated!

You describe the situation correctly: NeoJSON operates in 2 modes:

(1) generating generic Arrays and Dictionaries (just like the spec)

or

(2) optionally mapping, stream based, directly, without intermediary representation, into domain instances (given a predefined static mapping)

Using NeoJSONObject instead of Dictionary is just a convenience (it being a more JS like object).

There is indeed another approach, using dynamic typing, that is not specifically supported in NeoJSON.

As you remark yourself, that requires an intermediary representation, so one could just as well use option (1) and convert 'manually' in a second pass.

That being said, it could/would be a nice addition to offer some support for this. Although there are multiple slightly different approaches. For example, there also is

{
  "Point" : { "x" : 1, "y" : 2 }
}

Instead of

{
  "_type" : "Point",
  "x" : 1,
  "y" : 2
}

The key for the type/class can be different, the value can be anything, even 'com.java.graphics.Point'.

Note also that typically, there are no collection types (and these would be way more complicated).

Sven

> --
> Eric




--
Eric
Reply | Threaded
Open this post in threaded view
|

Re: NeoJSON Custom Mapping Question

darth-cheney
Thanks Norbert, that's great. I won't be able to get back to this project for another week or two (some stuff has come up) but will let you know if I have any questions.

On Thu, Jan 10, 2019 at 2:52 AM Norbert Hartl via Pharo-users <[hidden email]> wrote:
Hi Eric,

I‘m late to the game but I‘m interested to talk abd cooperate. I implemented JSON schema  [1] not long ago. This can also be used to add a mapping from object to json. It has also a type key inside the json object. I did it with an intermediate object. The JSONSchemaDefinition is a super type of all JSON schema types meaning one json object containing all keys of all types. I read the super type and then with a visitor
I explode this super type into concrete type objects. As you say on writing I use the concrete objects. The asymmetry is also a bit tricky but in the end the best approach so far. So if you like we can talk. I think that it could be a good idea to think about how JSON Schema and JSON LD could fit together.

Norbert 



Am 09.01.2019 um 22:27 schrieb Eric Gade <[hidden email]>:

Hi Sven, thanks for the response.

Unfortunately since I'm working with a standard here, there is always going to be an explicit `"type": <foo>` attribute rather than having the key itself be the name of the type. I guess I'll stick to the two-pass read solution. But the writing solution can use the mapping so that saves a lot of trouble!
 

On Wed, Jan 9, 2019 at 3:14 PM Sven Van Caekenberghe <[hidden email]> wrote:
Eric,

> On 9 Jan 2019, at 00:34, Eric Gade <[hidden email]> wrote:
>
> Hey guys,
>
> I'm toying with the idea of a Pharo implementation of ActivityStreams (https://www.w3.org/TR/activitystreams-core/#introduction) which is based on a subset of JSON called JSON-LD (for "linked data").
>
> While I do not believe this would entail creating a whole JSON-LD implementation, I do have a question about how to implement reading/writing/mapping to and from the core set of object types in the specification.
>
> I am aware that giving any class #neoJsonMapping allows one to define the specifics of the mapping to and from objects of that class (I think this is super cool, by the way). The issue with ActivityStreams objects is that the "type" of object they represent is in the JSON itself as the property "type," for example:
>
> ```
> {
> "@context": "https://www.w3.org/ns/activitystreams",
> "name": "A simple note",
> "type": "Note",
> "id": "http://www.test.example/notes/1",
> "content": "A simple note",
> "replies": {
>      "type": "Collection",
>      "totalItems": 1,
>      "items": [
>          {
>              "name": "A response to the note",
>              "type": "Note",
>              "content": "A response to the note",
>              "inReplyTo": "http://www.test.example/notes/1"
>          }
>      ]
> }
> }
> ```
>
> This refers to a top level ActivityStream object of type "Note" which we maybe want to parse into something like `ASNote`.
>
> My core question is this: how can we get a NeoJSONReader to determine which type of object to create by checking for a "type" property as soon as possible in the process? Because this property can show up in any order, we won't know what type of object to create until it is encountered. What is the best strategy there?
>
> Does the complexity suggest that we should simply parse incoming ActivityStream objects into the collection-and-dict default mapping and deal with creating the appropriate objects after that? In this case we could still use the custom mapping for the writing, which is not a problem since the "type" field will be associated with the class (ie, ASNote class >> #type will respond with 'Note' and we can go from there).
>
> Any thoughts or suggestions much appreciated!

You describe the situation correctly: NeoJSON operates in 2 modes:

(1) generating generic Arrays and Dictionaries (just like the spec)

or

(2) optionally mapping, stream based, directly, without intermediary representation, into domain instances (given a predefined static mapping)

Using NeoJSONObject instead of Dictionary is just a convenience (it being a more JS like object).

There is indeed another approach, using dynamic typing, that is not specifically supported in NeoJSON.

As you remark yourself, that requires an intermediary representation, so one could just as well use option (1) and convert 'manually' in a second pass.

That being said, it could/would be a nice addition to offer some support for this. Although there are multiple slightly different approaches. For example, there also is

{
  "Point" : { "x" : 1, "y" : 2 }
}

Instead of

{
  "_type" : "Point",
  "x" : 1,
  "y" : 2
}

The key for the type/class can be different, the value can be anything, even 'com.java.graphics.Point'.

Note also that typically, there are no collection types (and these would be way more complicated).

Sven

> --
> Eric




--
Eric


--
Eric