complex json parsing.

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

complex json parsing.

Roelof
Hello,

I try to make a  app which displays images from a external api .

I was hoping I could do it with only 1 json parsing but to get the right size of images I need more then 1

Right now I have two classes with fromJson methods

Painting class >> fromJSON: json
    | instance |
    instance := self new.
    instance
        title: (json at: #title);
        painter: (json at: #principalOrFirstMaker);
        imageUrl: ((json at: #webImage) at: #url).
    ^ instance


Paintings class > fromJSON: json

fromJSON: json
    | instance artObjects |
    instance := self new.
    artObjects := json at: #artObjects.
    artObjects
        do:
            [ :eachArtObject | instance addPainting: (Painting fromJSON: eachArtObject) ].
    ^ instance

but to get it working right , I have to do it like this :

Painting class >> fromJSON: json
    | instance |
    instance := self new.
    instance
        title: (json at: #objectNumber);
   ^ instance


but the problem is now I need a second call to this url : 
https://www.rijksmuseum.nl/api/nl/collection/<<objectNumber>/tiles?key=[API_KEY]&format=json


and then need a new imageUrl out of it. 
and then have one object with objectNumber and the new url

Also I have to make a call to this url : 
https://www.rijksmuseum.nl/api/nl/collection/<<<objectNumber>>?key=[API_KEY]&format=json

and also get some data out of it so that a painting will be one object with the objectNumber, new imageurl 
and some data out of the last call 

Do I need to make some more object to get this working or is there a way I can change the code I use now
to get things working 

Regards, 

Roelof 


Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Ben Coman


On Sun, 10 Mar 2019 at 04:55, Roelof Wobben <[hidden email]> wrote:
Hello,

I try to make a  app which displays images from a external api .

I was hoping I could do it with only 1 json parsing but to get the right size of images I need more then 1

Right now I have two classes with fromJson methods

Painting class >> fromJSON: json
    | instance |
    instance := self new.
    instance
        title: (json at: #title);
        painter: (json at: #principalOrFirstMaker);
        imageUrl: ((json at: #webImage) at: #url).
    ^ instance


Paintings class > fromJSON: json

fromJSON: json
    | instance artObjects |
    instance := self new.
    artObjects := json at: #artObjects.
    artObjects
        do:
            [ :eachArtObject | instance addPainting: (Painting fromJSON: eachArtObject) ].
    ^ instance

but to get it working right , I have to do it like this :

Painting class >> fromJSON: json
    | instance |
    instance := self new.
    instance
        title: (json at: #objectNumber);
   ^ instance


but the problem is now I need a second call to this url : 
https://www.rijksmuseum.nl/api/nl/collection/<<objectNumber>/tiles?key=[API_KEY]&format=json


and then need a new imageUrl out of it. 
and then have one object with objectNumber and the new url

Also I have to make a call to this url : 
https://www.rijksmuseum.nl/api/nl/collection/<<<objectNumber>>?key=[API_KEY]&format=json

and also get some data out of it so that a painting will be one object with the objectNumber, new imageurl 
and some data out of the last call 

Do I need to make some more object to get this working or is there a way I can change the code I use now
to get things working 

Regards, 

Roelof 

Its hard to tell from your description.
You say "the problem is now I need a second call" but you've not shown the code that does the calling,
and you've not shown the first call, or where you are looping through multiple paintings (I'm guessing that is the requirement)

You say "Do I need to make some more object", but are you referring to more Paintings objects or more Painting objects?

So speaking only very generally, this might be implemented in two ways...
A1. Request the json of paintings into a Paintings object  
A2 Loop that json requesting each individual painting json to fully initialized Painting object 

or...
B1. Request the json of paintings into a Paintings object 
B2. Loop that json create empty Painting objects
B3. Iterate those Painting objects with each one requesting its own json from the server, and can do multiple requests if necessary

And btw, very very generally, if you see even a hint of one way to do it - do it that way first and see what insights arise from that to improve it.   
Particularly when exploring a new domain like an unfamiliar API, the usual approach is...
1. Make it work
2. Make it right
3. Make it fast
Use whatever hackery you need to get step 1 done before moving to step 2, which you are inquiring about. And often you don't even need step 3.

cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Roelof
Op 10-3-2019 om 02:13 schreef Ben Coman:


On Sun, 10 Mar 2019 at 04:55, Roelof Wobben <[hidden email]> wrote:
Hello,

I try to make a  app which displays images from a external api .

I was hoping I could do it with only 1 json parsing but to get the right size of images I need more then 1

Right now I have two classes with fromJson methods

Painting class >> fromJSON: json
    | instance |
    instance := self new.
    instance
        title: (json at: #title);
        painter: (json at: #principalOrFirstMaker);
        imageUrl: ((json at: #webImage) at: #url).
    ^ instance


Paintings class > fromJSON: json

fromJSON: json
    | instance artObjects |
    instance := self new.
    artObjects := json at: #artObjects.
    artObjects
        do:
            [ :eachArtObject | instance addPainting: (Painting fromJSON: eachArtObject) ].
    ^ instance

but to get it working right , I have to do it like this :

Painting class >> fromJSON: json
    | instance |
    instance := self new.
    instance
        title: (json at: #objectNumber);
   ^ instance


but the problem is now I need a second call to this url : 
https://www.rijksmuseum.nl/api/nl/collection/<<objectNumber>/tiles?key=[API_KEY]&format=json


and then need a new imageUrl out of it. 
and then have one object with objectNumber and the new url

Also I have to make a call to this url : 
https://www.rijksmuseum.nl/api/nl/collection/<<<objectNumber>>?key=[API_KEY]&format=json

and also get some data out of it so that a painting will be one object with the objectNumber, new imageurl 
and some data out of the last call 

Do I need to make some more object to get this working or is there a way I can change the code I use now
to get things working 

Regards, 

Roelof 

Its hard to tell from your description.
You say "the problem is now I need a second call" but you've not shown the code that does the calling,
and you've not shown the first call, or where you are looping through multiple paintings (I'm guessing that is the requirement)

You say "Do I need to make some more object", but are you referring to more Paintings objects or more Painting objects?

So speaking only very generally, this might be implemented in two ways...
A1. Request the json of paintings into a Paintings object  
A2 Loop that json requesting each individual painting json to fully initialized Painting object 

or...
B1. Request the json of paintings into a Paintings object 
B2. Loop that json create empty Painting objects
B3. Iterate those Painting objects with each one requesting its own json from the server, and can do multiple requests if necessary

And btw, very very generally, if you see even a hint of one way to do it - do it that way first and see what insights arise from that to improve it.   
Particularly when exploring a new domain like an unfamiliar API, the usual approach is...
1. Make it work
2. Make it right
3. Make it fast
Use whatever hackery you need to get step 1 done before moving to step 2, which you are inquiring about. And often you don't even need step 3.

cheers -ben

Hello Ben

Sorry for the general thing. Im thinking how I can solve things before begin with coding

Right now the first call is :

getImages
    | json |
    json := NeoJSONReader
        fromString:
            (ZnEasy
                get:
                    'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True')
                contents.
    ^ self class fromJSON: json

but as I try to say I do not see how and where to place the second call and process the json
as far as I know I cannot make a second fromJSON in a paintings object

if you need I can make a fileout of place the image so far on for example dropbox

Roelof

Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Roelof
I could do something like this :

getImages
    | json numbers json2  |
    json := NeoJSONReader
        fromString:
            (ZnEasy
                get:
                    'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True')
                contents.
    numbers := self class fromJSON: json
    numbers do: [each |  json :=  .........
                                           imageData = self class ???


can I for example name the function fromJson2 or fromJSONFromLink2  ?


Op 10-3-2019 om 10:25 schreef Roelof Wobben:
Op 10-3-2019 om 02:13 schreef Ben Coman:


On Sun, 10 Mar 2019 at 04:55, Roelof Wobben <[hidden email]> wrote:
Hello,

I try to make a  app which displays images from a external api .

I was hoping I could do it with only 1 json parsing but to get the right size of images I need more then 1

Right now I have two classes with fromJson methods

Painting class >> fromJSON: json
    | instance |
    instance := self new.
    instance
        title: (json at: #title);
        painter: (json at: #principalOrFirstMaker);
        imageUrl: ((json at: #webImage) at: #url).
    ^ instance


Paintings class > fromJSON: json

fromJSON: json
    | instance artObjects |
    instance := self new.
    artObjects := json at: #artObjects.
    artObjects
        do:
            [ :eachArtObject | instance addPainting: (Painting fromJSON: eachArtObject) ].
    ^ instance

but to get it working right , I have to do it like this :

Painting class >> fromJSON: json
    | instance |
    instance := self new.
    instance
        title: (json at: #objectNumber);
   ^ instance


but the problem is now I need a second call to this url : 
https://www.rijksmuseum.nl/api/nl/collection/<<objectNumber>/tiles?key=[API_KEY]&format=json


and then need a new imageUrl out of it. 
and then have one object with objectNumber and the new url

Also I have to make a call to this url : 
https://www.rijksmuseum.nl/api/nl/collection/<<<objectNumber>>?key=[API_KEY]&format=json

and also get some data out of it so that a painting will be one object with the objectNumber, new imageurl 
and some data out of the last call 

Do I need to make some more object to get this working or is there a way I can change the code I use now
to get things working 

Regards, 

Roelof 

Its hard to tell from your description.
You say "the problem is now I need a second call" but you've not shown the code that does the calling,
and you've not shown the first call, or where you are looping through multiple paintings (I'm guessing that is the requirement)

You say "Do I need to make some more object", but are you referring to more Paintings objects or more Painting objects?

So speaking only very generally, this might be implemented in two ways...
A1. Request the json of paintings into a Paintings object  
A2 Loop that json requesting each individual painting json to fully initialized Painting object 

or...
B1. Request the json of paintings into a Paintings object 
B2. Loop that json create empty Painting objects
B3. Iterate those Painting objects with each one requesting its own json from the server, and can do multiple requests if necessary

And btw, very very generally, if you see even a hint of one way to do it - do it that way first and see what insights arise from that to improve it.   
Particularly when exploring a new domain like an unfamiliar API, the usual approach is...
1. Make it work
2. Make it right
3. Make it fast
Use whatever hackery you need to get step 1 done before moving to step 2, which you are inquiring about. And often you don't even need step 3.

cheers -ben

Hello Ben

Sorry for the general thing. Im thinking how I can solve things before begin with coding

Right now the first call is :

getImages
    | json |
    json := NeoJSONReader
        fromString:
            (ZnEasy
                get:
                    'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True')
                contents.
    ^ self class fromJSON: json

but as I try to say I do not see how and where to place the second call and process the json
as far as I know I cannot make a second fromJSON in a paintings object

if you need I can make a fileout of place the image so far on for example dropbox

Roelof


Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Ben Coman


On Sun, 10 Mar 2019 at 17:55, Roelof Wobben <[hidden email]> wrote:
I could do something like this :

getImages
    | json numbers json2  |
    json := NeoJSONReader
        fromString:
            (ZnEasy
                get:
                    'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True')
                contents.
    numbers := self class fromJSON: json
    numbers do: [each |  json :=  .........
                                           imageData = self class ???

I'm not sure what "numbers" refers to. It seems a quite non-domain related identifier.
To understand the domain, pasting the json contents of your link above into https://jsonformatter.curiousconcept.com/
and collapsing objects I see the structure is...
{  
   "elapsedMilliseconds":0,
   "count":4782,
   "countFacets":{  
      "hasimage":4359,
      "ondisplay":748
   },
   "artObjects":[ + ] ,
   "facets":[ + ]
}

where artObjects is an array with each art object like this==>
      {  
         "links":{  
         },
         "id":"nl-SK-A-3580",
         "objectNumber":"SK-A-3580",
         "title":"De Singelbrug bij de Paleisstraat in Amsterdam",
         "hasImage":true,
         "principalOrFirstMaker":"George Hendrik Breitner",
         "longTitle":"De Singelbrug bij de Paleisstraat in Amsterdam, George Hendrik Breitner, 1898",
         "showImage":true,
         "permitDownload":true,
         "webImage":{  
            "guid":"cd15e2fa-b1f6-41f4-9e9e-c2ca87abcca3",
            "offsetPercentageX":0,
            "offsetPercentageY":0,
            "width":2880,
            "height":1897,
         },
         "headerImage":{  
            "guid":"fef39f74-9783-44c9-acae-69eac3a76b01",
            "offsetPercentageX":0,
            "offsetPercentageY":0,
            "width":1920,
            "height":460,
         },
         "productionPlaces":[  
            "Amsterdam"
         ]
      }

So evaluating the following in Playground... 
    collectionJson := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: collectionJson.
    paintings inspect.

then cleaning the DNU errors as i went, 
starting with your original code (which was good btw)...

    Paintings class >>  fromJSON: json
        | instance artObjects |
        instance := self new.
        artObjects := json at: #artObjects.
        artObjects
            do:
                [ :eachArtObject | instance addPainting: (Painting fromJSON: eachArtObject) ].
        ^ instance

    Painting class >> fromJSON: json
        | instance |
        instance := self new.
        instance
            title: (json at: #title);
            painter: (json at: #principalOrFirstMaker);
            imageUrl: ((json at: #webImage) at: #url).
        ^ instance

I added (roughly in order that each DNU occurred)...

    Painting >> title: aString 
title := aString

    Painting >> painter: aString 
painter := aString

    Painting >> imageUrl: aString 
imageUrl := aString

    Paintings >> addPainting: aPainting 
paintings := paintings ifNil: [ OrderedCollection new ].
paintings add: aPainting.

I get an inspector on a list of paintings and can drill down to a painting and see each has the expected data,
and just to round things off...

    Painting >> title
^ title

    Painting >> printOn: aStream
super printOn: aStream.
aStream << ' (' << self title << ')'

helps distinguish each item in the Inspector.


Now to extend the Playground code to download and display a painting, in Playground I evaluated...

    painting := paintings first.
    imageResponse := ZnEasy get: painting imageUrl.
    image := ImageReadWriter formFromStream: imageResponse entity readStream.
    image inspect.

    Paintings >> first
^ paintings first

    Painting >> imageUrl
^ imageUrl

and an inspector on the `image` variable displays the painting on the Morph tab.


Now to mold the IDE to your domain... 
using Spotter to browser gtInspector* methods, a promising find is... AbstractFileReference>>gitInspectorJpegIn:
from which I produced...

    Painting  >>  gtInspectorJpegIn: composite
<gtInspectorPresentationOrder: 0>
composite morph
title: 'Painting';
display: [ ImageReadWriter formFromStream: self imageEntity readStream ] 

Then inspecting the `paintings` variable and drilling down to a painting 
pops up a DNU #imageEntity, which can be resolved by...

    Painting >> imageEntity
^ imageEntity ifNil: [ imageEntity := (ZnEasy get: self imageUrl) entity ].

and you get to see the painting shown in the Inspector.


Now if I understand your question... "Do I need to make some more object to get this working..."
I'd say... No. You only want one object for each painting. Once you have a painting object, it should handle all getting all further data it needs for itself.
You don't want duplicate objects each having half of the data.


can I for example name the function fromJson2 or fromJSONFromLink2  ?

By naming convention #fromJson: implies it sits on the class side.
To get further data for an existing object you want an instance-side method, maybe named #getLink2Json. 

HTH, 
cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Roelof
Op 10-3-2019 om 16:19 schreef Ben Coman:


On Sun, 10 Mar 2019 at 17:55, Roelof Wobben <[hidden email]> wrote:
I could do something like this :

getImages
    | json numbers json2  |
    json := NeoJSONReader
        fromString:
            (ZnEasy
                get:
                    'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True')
                contents.
    numbers := self class fromJSON: json
    numbers do: [each |  json :=  .........
                                           imageData = self class ???

I'm not sure what "numbers" refers to. It seems a quite non-domain related identifier.
To understand the domain, pasting the json contents of your link above into https://jsonformatter.curiousconcept.com/
and collapsing objects I see the structure is...
{  
   "elapsedMilliseconds":0,
   "count":4782,
   "countFacets":{  
      "hasimage":4359,
      "ondisplay":748
   },
   "artObjects":[ + ] ,
   "facets":[ + ]
}

where artObjects is an array with each art object like this==>
      {  
         "links":{  
         },
         "id":"nl-SK-A-3580",
         "objectNumber":"SK-A-3580",
         "title":"De Singelbrug bij de Paleisstraat in Amsterdam",
         "hasImage":true,
         "principalOrFirstMaker":"George Hendrik Breitner",
         "longTitle":"De Singelbrug bij de Paleisstraat in Amsterdam, George Hendrik Breitner, 1898",
         "showImage":true,
         "permitDownload":true,
         "webImage":{  
            "guid":"cd15e2fa-b1f6-41f4-9e9e-c2ca87abcca3",
            "offsetPercentageX":0,
            "offsetPercentageY":0,
            "width":2880,
            "height":1897,
         },
         "headerImage":{  
            "guid":"fef39f74-9783-44c9-acae-69eac3a76b01",
            "offsetPercentageX":0,
            "offsetPercentageY":0,
            "width":1920,
            "height":460,
         },
         "productionPlaces":[  
            "Amsterdam"
         ]
      }

So evaluating the following in Playground... 
    collectionJson := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: collectionJson.
    paintings inspect.

then cleaning the DNU errors as i went, 
starting with your original code (which was good btw)...

    Paintings class >>  fromJSON: json
        | instance artObjects |
        instance := self new.
        artObjects := json at: #artObjects.
        artObjects
            do:
                [ :eachArtObject | instance addPainting: (Painting fromJSON: eachArtObject) ].
        ^ instance

    Painting class >> fromJSON: json
        | instance |
        instance := self new.
        instance
            title: (json at: #title);
            painter: (json at: #principalOrFirstMaker);
            imageUrl: ((json at: #webImage) at: #url).
        ^ instance

I added (roughly in order that each DNU occurred)...

    Painting >> title: aString 
title := aString

    Painting >> painter: aString 
painter := aString

    Painting >> imageUrl: aString 
imageUrl := aString

    Paintings >> addPainting: aPainting 
paintings := paintings ifNil: [ OrderedCollection new ].
paintings add: aPainting.

I get an inspector on a list of paintings and can drill down to a painting and see each has the expected data,
and just to round things off...

    Painting >> title
^ title

    Painting >> printOn: aStream
super printOn: aStream.
aStream << ' (' << self title << ')'

helps distinguish each item in the Inspector.


Now to extend the Playground code to download and display a painting, in Playground I evaluated...

    painting := paintings first.
    imageResponse := ZnEasy get: painting imageUrl.
    image := ImageReadWriter formFromStream: imageResponse entity readStream.
    image inspect.

    Paintings >> first
^ paintings first

    Painting >> imageUrl
^ imageUrl

and an inspector on the `image` variable displays the painting on the Morph tab.


Now to mold the IDE to your domain... 
using Spotter to browser gtInspector* methods, a promising find is... AbstractFileReference>>gitInspectorJpegIn:
from which I produced...

    Painting  >>  gtInspectorJpegIn: composite
<gtInspectorPresentationOrder: 0>
composite morph
title: 'Painting';
display: [ ImageReadWriter formFromStream: self imageEntity readStream ] 

Then inspecting the `paintings` variable and drilling down to a painting 
pops up a DNU #imageEntity, which can be resolved by...

    Painting >> imageEntity
^ imageEntity ifNil: [ imageEntity := (ZnEasy get: self imageUrl) entity ].

and you get to see the painting shown in the Inspector.


Now if I understand your question... "Do I need to make some more object to get this working..."
I'd say... No. You only want one object for each painting. Once you have a painting object, it should handle all getting all further data it needs for itself.
You don't want duplicate objects each having half of the data.


can I for example name the function fromJson2 or fromJSONFromLink2  ?

By naming convention #fromJson: implies it sits on the class side.
To get further data for an existing object you want an instance-side method, maybe named #getLink2Json. 

HTH, 
cheers -ben


oke

numbers schould be the objectNumbers because for the rest I need them


I use a instance variable called painting to hold the data.


so I can change the call to get data for the painting to :

Painting class >> fromJSON: json
        | instance |
        instance := self new.
        instance
            objectNumber: ((json at: #objectNumber).
        ^ instance


so numbers should be a collection of 10 ObjectNumbers

so I should use a loop to make a call to the second api call

 getImages
    | json numbers  |
    json := NeoJSONReader
        fromString:
            (ZnEasy
                get:
                    'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True')
                contents.
    numbers painting := self class fromJSON: json  
    numbers  do: [each |  json :=  call to the second url.here I used each 
                                           imageData = self class getLink2Json
                                           json2 := call to the 3th url
                                           otherData := self class  getLink3Json
                                          

Then  I can  I hope on every json method fill in the data I need

Do I understand you well

Roelof






Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Ben Coman


On Sun, 10 Mar 2019 at 23:40, Roelof Wobben <[hidden email]> wrote:
Op 10-3-2019 om 16:19 schreef Ben Coman:


On Sun, 10 Mar 2019 at 17:55, Roelof Wobben <[hidden email]> wrote:
I could do something like this :

getImages
    | json numbers json2  |
    json := NeoJSONReader
        fromString:
            (ZnEasy
                get:
                    'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True')
                contents.
    numbers := self class fromJSON: json
    numbers do: [each |  json :=  .........
                                           imageData = self class ???

I'm not sure what "numbers" refers to. It seems a quite non-domain related identifier.
To understand the domain, pasting the json contents of your link above into https://jsonformatter.curiousconcept.com/
and collapsing objects I see the structure is...
<snip> 

Did you work through this example I provided?   Please do so.
In a clean image...
Start with only your original code (which was good btw)...

    Paintings class >>  fromJSON: json
        | instance artObjects |
        instance := self new.
        artObjects := json at: #artObjects.
        artObjects
            do:
                [ :eachArtObject | instance addPainting: (Painting fromJSON: eachArtObject) ].
        ^ instance

except objectNumber(s) seem associated with each painting, so should be stored within the painting object, as ADDED here... 

 Painting class >> fromJSON: json
        | instance |
        instance := self new.
        instance
            objectNumber: (json at: #objectNumber);     "<<<<<<<<ADDED"
            title: (json at: #title);
            painter: (json at: #principalOrFirstMaker);
            imageUrl: ((json at: #webImage) at: #url).
        ^ instance

Evaluate the following in Playground... 
    collectionJson := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: collectionJson.
    paintings inspect.
then clean the DNU errors as they occur (i.e. click the <Create> button shown by the debugger, and fill in as follows...)  
when you save the methods, choose <Declare new instance variable>"  

    Painting >> objectNumber: aString 
objectNumber := aString  
    Painting >> title: aString 
title := aString     

    Painting >> painter: aString 
painter := aString

    Painting >> imageUrl: aString 
imageUrl := aString

    Paintings >> addPainting: aPainting 
paintings := paintings ifNil: [ OrderedCollection new ].
paintings add: aPainting.

and an inspector will appear showing a list of paintings, which you can drill down to a painting and see each has the expected data.
Now just to round things off...

    Painting >> title
^ title

    Painting >> printOn: aStream
super printOn: aStream.
aStream << ' (' << self title << ')'

to help distinguish each item in the Inspector.


To extend the Playground code to download and display a painting, in Playground evaluate...

    painting := paintings first.
    imageResponse := ZnEasy get: painting imageUrl.
    image := ImageReadWriter formFromStream: imageResponse entity readStream.
    image inspect.

    Paintings >> first
^ paintings first

    Painting >> imageUrl
^ imageUrl

and an inspector on the `image` variable displays the painting on the Morph tab.


Now to mold the IDE to your domain... 
using Spotter to browser gtInspector* methods, a promising find is... AbstractFileReference>>gitInspectorJpegIn:
from which I produced...

    Painting  >>  gtInspectorJpegIn: composite
<gtInspectorPresentationOrder: 0>
composite morph
title: 'Painting';
display: [ ImageReadWriter formFromStream: self imageEntity readStream ] 

Then inspecting the `paintings` variable and drilling down to a painting 
pops up a DNU #imageEntity, which can be resolved by...

    Painting >> imageEntity
^ imageEntity ifNil: [ imageEntity := (ZnEasy get: self imageUrl) entity ].

and you get to see the painting shown in the Inspector.


Now if I understand your question... "Do I need to make some more object to get this working..."
I'd say... No. You only want one object for each painting. Once you have a painting object, it should handle all getting all further data it needs for itself.
You don't want duplicate objects each having half of the data.


can I for example name the function fromJson2 or fromJSONFromLink2  ?

By naming convention #fromJson: implies it sits on the class side.
To get further data for an existing object you want an instance-side method, maybe named #getLink2Json. 

HTH, 
cheers -ben


oke

numbers should be the objectNumbers because for the rest I need them


 


I use a instance variable called painting to hold the data.


so I can change the call to get data for the painting to :

Painting class >> fromJSON: json
        | instance |
        instance := self new.
        instance
            objectNumber: ((json at: #objectNumber).
        ^ instance


so numbers should be a collection of 10 ObjectNumbers

I'm presuming that 10 objectNumbers relate to 10 different paintings,
in which case, No, you should never need to deal with a collection of 10 objectNumbers.
What you should have is a collection of painting-objects which know their own objectNumber
and then iterate that collection sending each painting-object the message "getLink2Json".

 
so I should use a loop to make a call to the second api call

In Pharo OO approach, you don't "loop" on the "calls", 
you "iterate" on the "objects" asking them to use their own data to do the stuff they know how to do.
i.e. Let each object take care of itself.

Which class is getImages defined on?  That is really important to understand your proposed solution. 
I'm just presuming its defined on the class side of Paintings since your are referring to the same URL as before.

 getImages
    | json numbers  |
    json := NeoJSONReader
        fromString:
            (ZnEasy
                get:
                    'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True')
                contents.
    numbers painting := self class fromJSON: json  

btw, it won't work with two identifiers "numbers painting" to the left of the assignment symbol.  Space here bad.
 

    numbers  do: [each |  json :=  call to the second url.here I used each 
                                           imageData = self class getLink2Json 
                                           json2 := call to the 3th url
                                           otherData := self class  getLink3Json
                                       

no :)    the "self class" seems wrong.
getting extra data about a painting should be handled by the instance side of its painting-object.

 
Then  I can  I hope on every json method fill in the data I need

Do I understand you well

I think not so well yet :)   (or else I am missing something)
Keep trying.  It will change the way you think about programming.

Please work though the example I provided.
cheers -ben

 
Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Ben Coman
In reply to this post by Ben Coman


On Sun, 10 Mar 2019 at 23:19, Ben Coman <[hidden email]> wrote:

Now to mold the IDE to your domain... 
using Spotter to browser gtInspector* methods, a promising find is... AbstractFileReference>>gitInspectorJpegIn:
from which I produced...

    Painting  >>  gtInspectorJpegIn: composite
<gtInspectorPresentationOrder: 0>
composite morph
title: 'Painting';
display: [ ImageReadWriter formFromStream: self imageEntity readStream ] 

@all, can someone suggest a better #display: block that would shrink the image to the window size,
and/or zooming.

cheers -ben 
Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Roelof
In reply to this post by Ben Coman
Op 10-3-2019 om 17:45 schreef Ben Coman:


On Sun, 10 Mar 2019 at 23:40, Roelof Wobben <[hidden email]> wrote:
Op 10-3-2019 om 16:19 schreef Ben Coman:


On Sun, 10 Mar 2019 at 17:55, Roelof Wobben <[hidden email]> wrote:
I could do something like this :

getImages
    | json numbers json2  |
    json := NeoJSONReader
        fromString:
            (ZnEasy
                get:
                    'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True')
                contents.
    numbers := self class fromJSON: json
    numbers do: [each |  json :=  .........
                                           imageData = self class ???

I'm not sure what "numbers" refers to. It seems a quite non-domain related identifier.
To understand the domain, pasting the json contents of your link above into https://jsonformatter.curiousconcept.com/
and collapsing objects I see the structure is...
<snip> 

Did you work through this example I provided?   Please do so.
In a clean image...
Start with only your original code (which was good btw)...

    Paintings class >>  fromJSON: json
        | instance artObjects |
        instance := self new.
        artObjects := json at: #artObjects.
        artObjects
            do:
                [ :eachArtObject | instance addPainting: (Painting fromJSON: eachArtObject) ].
        ^ instance

except objectNumber(s) seem associated with each painting, so should be stored within the painting object, as ADDED here... 

 Painting class >> fromJSON: json
        | instance |
        instance := self new.
        instance
            objectNumber: (json at: #objectNumber);     "<<<<<<<<ADDED"
            title: (json at: #title);
            painter: (json at: #principalOrFirstMaker);
            imageUrl: ((json at: #webImage) at: #url).
        ^ instance

Evaluate the following in Playground... 
    collectionJson := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: collectionJson.
    paintings inspect.
then clean the DNU errors as they occur (i.e. click the <Create> button shown by the debugger, and fill in as follows...)  
when you save the methods, choose <Declare new instance variable>"  

    Painting >> objectNumber: aString 
objectNumber := aString  
    Painting >> title: aString 
title := aString     

    Painting >> painter: aString 
painter := aString

    Painting >> imageUrl: aString 
imageUrl := aString

    Paintings >> addPainting: aPainting 
paintings := paintings ifNil: [ OrderedCollection new ].
paintings add: aPainting.

and an inspector will appear showing a list of paintings, which you can drill down to a painting and see each has the expected data.
Now just to round things off...

    Painting >> title
^ title

    Painting >> printOn: aStream
super printOn: aStream.
aStream << ' (' << self title << ')'

to help distinguish each item in the Inspector.


To extend the Playground code to download and display a painting, in Playground evaluate...

    painting := paintings first.
    imageResponse := ZnEasy get: painting imageUrl.
    image := ImageReadWriter formFromStream: imageResponse entity readStream.
    image inspect.

    Paintings >> first
^ paintings first

    Painting >> imageUrl
^ imageUrl

and an inspector on the `image` variable displays the painting on the Morph tab.


Now to mold the IDE to your domain... 
using Spotter to browser gtInspector* methods, a promising find is... AbstractFileReference>>gitInspectorJpegIn:
from which I produced...

    Painting  >>  gtInspectorJpegIn: composite
<gtInspectorPresentationOrder: 0>
composite morph
title: 'Painting';
display: [ ImageReadWriter formFromStream: self imageEntity readStream ] 

Then inspecting the `paintings` variable and drilling down to a painting 
pops up a DNU #imageEntity, which can be resolved by...

    Painting >> imageEntity
^ imageEntity ifNil: [ imageEntity := (ZnEasy get: self imageUrl) entity ].

and you get to see the painting shown in the Inspector.


Now if I understand your question... "Do I need to make some more object to get this working..."
I'd say... No. You only want one object for each painting. Once you have a painting object, it should handle all getting all further data it needs for itself.
You don't want duplicate objects each having half of the data.


can I for example name the function fromJson2 or fromJSONFromLink2  ?

By naming convention #fromJson: implies it sits on the class side.
To get further data for an existing object you want an instance-side method, maybe named #getLink2Json. 

HTH, 
cheers -ben


oke

numbers should be the objectNumbers because for the rest I need them


 


I use a instance variable called painting to hold the data.


so I can change the call to get data for the painting to :

Painting class >> fromJSON: json
        | instance |
        instance := self new.
        instance
            objectNumber: ((json at: #objectNumber).
        ^ instance


so numbers should be a collection of 10 ObjectNumbers

I'm presuming that 10 objectNumbers relate to 10 different paintings,
in which case, No, you should never need to deal with a collection of 10 objectNumbers.
What you should have is a collection of painting-objects which know their own objectNumber
and then iterate that collection sending each painting-object the message "getLink2Json".

 
so I should use a loop to make a call to the second api call

In Pharo OO approach, you don't "loop" on the "calls", 
you "iterate" on the "objects" asking them to use their own data to do the stuff they know how to do.
i.e. Let each object take care of itself.

Which class is getImages defined on?  That is really important to understand your proposed solution. 
I'm just presuming its defined on the class side of Paintings since your are referring to the same URL as before.

 getImages
    | json numbers  |
    json := NeoJSONReader
        fromString:
            (ZnEasy
                get:
                    'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True')
                contents.
    numbers painting := self class fromJSON: json  

btw, it won't work with two identifiers "numbers painting" to the left of the assignment symbol.  Space here bad.
 

    numbers  do: [each |  json :=  call to the second url.here I used each 
                                           imageData = self class getLink2Json 
                                           json2 := call to the 3th url
                                           otherData := self class  getLink3Json
                                       

no :)    the "self class" seems wrong.
getting extra data about a painting should be handled by the instance side of its painting-object.

 
Then  I can  I hope on every json method fill in the data I need

Do I understand you well

I think not so well yet :)   (or else I am missing something)
Keep trying.  It will change the way you think about programming.

Please work though the example I provided.
cheers -ben

 

I did and it worked the same as my code
Still I do not see how to use then the objectNumber in a second call to the api
for example

lets say we have only 1 objectNumber now
for example
SK-C-5

then I need that one here :   
https://www.rijksmuseum.nl/api/nl/collection/SK-C-5?key=[API_KEY]&format=json

so I see a json response of this : 

{
  "elapsedMilliseconds": 27,
  "artObject": {
    "links": {
      "search": "https://www.rijksmuseum.nl/api/nl/collection"
    },
    "id": "nl-SK-C-5",
    "priref": "5216",
    "objectNumber": "SK-C-5",
    "language": "nl",
    "title": "Schutters van wijk II onder leiding van kapitein Frans Banninck Cocq, bekend als de ‘Nachtwacht’",
    "tags": [
      {
        "userId": 3530,
        "name": "margaret barrett my first collection",
        "lang": "en",
        "objectNumber": "SK-C-5"
      },
      // more results...
    ],
    "copyrightHolder": null,
    "webImage": {
      "guid": "92253da1-794d-49f4-9e3c-e4c160715f53",
      "offsetPercentageX": 50,
      "offsetPercentageY": 100,
      "width": 2500,
      "height": 2034,
      "url": "http://lh6.ggpht.com/wwx2vAS9DzFmmyeZefPjMtmCNOdjD80gvkXJcylloy40SiZOhdLHVddEZLBHtymHu53TcvqJLYZfZF7M-uvoMmG_wSI=s0"
    },
    "colors": [
      "#261808",
      // more results...
    ],
    "colorsWithNormalization": [
      {
        "originalHex": "#261808",
        "normalizedHex": "#000000"
      },
      // more results...
    ],
    "normalizedColors": [
      "#000000",
      // more results...
    ],
    "normalized32Colors": [
      "#000000",
      // more results...
    ],
    "titles": [
      "Officieren en andere schutters van wijk II in Amsterdam onder leiding van kapitein Frans Banninck Cocq en luitenant Willem van Ruytenburch, bekend als de ‘Nachtwacht’",
      "Het korporaalschap van kapitein Frans Banninck Cocq en luitenant Willem van Ruytenburch, bekend als de 'Nachtwacht'"
    ],
    "description": "Het korporaalschap van kapitein Frans Banninck Cocq en luitenant Willem van Ruytenburch, bekend als de 'Nachtwacht'. Schutters van de kloveniersdoelen uit een poort naar buiten tredend. Op een schild aangebracht naast de poort staan de namen van de afgebeelde personen: Frans Banninck Cocq, heer van purmerlant en Ilpendam, Capiteijn Willem van Ruijtenburch van Vlaerdingen, heer van Vlaerdingen, Lu[ij]tenant, Jan Visscher Cornelisen Vaendrich, Rombout Kemp Sergeant, Reijnier Engelen Sergeant, Barent Harmansen, Jan Adriaensen Keyser, Elbert Willemsen, Jan Clasen Leydeckers, Jan Ockersen, Jan Pietersen bronchorst, Harman Iacobsen wormskerck, Jacob Dircksen de Roy, Jan vander heede, Walich Schellingwou, Jan brugman, Claes van Cruysbergen, Paulus Schoonhoven. De schutters zijn gewapend met lansen, musketten en hellebaarden. Rechts de tamboer met een grote trommel. Tussen de soldaten links staat een meisje met een dode kip om haar middel, rechts een blaffende hond. Linksboven de vaandrig met de uitgestoken vaandel.",
    "labelText": null,
    "objectTypes": [
      "schilderij"
    ],
    "objectCollection": [
      "schilderijen"
    ],
    "makers": [
      {
        "name": "Rembrandt Harmensz. van Rijn",
        "unFixedName": "Rembrandt Harmensz. van Rijn",
        "placeOfBirth": "Leiden",
        "dateOfBirth": "1606-07-15",
        "dateOfBirthPrecision": null,
        "dateOfDeath": "1669-10-08",
        "dateOfDeathPrecision": null,
        "placeOfDeath": "Amsterdam",
        "occupation": [
          "prentmaker",
          "tekenaar",
          "schilder"
        ],
        "roles": [
          "schilder"
        ],
        "nationality": "Noord-Nederlands",
        "biography": null,
        "productionPlaces": [],
        "schoolStyles": [],
        "qualification": null
      }
    ],
    "principalMakers": [
      {
        "name": "Rembrandt Harmensz. van Rijn",
        "unFixedName": "Rembrandt Harmensz. van Rijn",
        "placeOfBirth": "Leiden",
        "dateOfBirth": "1606-07-15",
        "dateOfBirthPrecision": null,
        "dateOfDeath": "1669-10-08",
        "dateOfDeathPrecision": null,
        "placeOfDeath": "Amsterdam",
        "occupation": [
          "prentmaker",
          "tekenaar",
          "schilder"
        ],
        "roles": [
          "schilder"
        ],
        "nationality": "Noord-Nederlands",
        "biography": null,
        "productionPlaces": [],
        "schoolStyles": [],
        "qualification": null
      }
    ],
    "plaqueDescriptionDutch": "Rembrandts beroemdste en grootste doek werd gemaakt voor de Kloveniersdoelen. Dit was een van de verenigingsgebouwen van de Amsterdamse schutterij, de burgerwacht van de stad. \r\nRembrandt was de eerste die op een groepsportret de figuren in actie weergaf. De kapitein, in het zwart, geeft zijn luitenant opdracht dat de compagnie moet gaan marcheren. De schutters stellen zich op. Met behulp van licht vestigde Rembrandt de aandacht op belangrijke details, zoals het handgebaar van de kapitein en het kleine meisje op de achtergrond. Zij is de mascotte van de schutters.",
    "plaqueDescriptionEnglish": null,
    "principalMaker": "Rembrandt Harmensz. van Rijn",
    "artistRole": null,
    "associations": [],
    "acquisition": {
      "method": "bruikleen",
      "date": "1808-01-01T00:00:00Z",
      "creditLine": "Bruikleen van de gemeente Amsterdam"
    },
    "exhibitions": [],
    "materials": [
      "doek",
      "olieverf"
    ],
    "techniques": [],
    "productionPlaces": [],
    "dating": {
      "presentingDate": "ca. 1642",
      "sortingDate": 1642,
      "period": 17
    },
    "classification": {
      "iconClassIdentifier": [
        "45(+26)",
        "45C1",
        "48C7341",
        "31D11222",
        "45D12",
        "34B11"
      ],
      "motifs": [],
      "events": [],
      "periods": [],
      "places": [
        "Amsterdam"
      ],
      "people": []
    },
    "hasImage": true,
    "historicalPersons": [],
    "inscriptions": [
      "Rembrandt f 1642",
      "signatuur en datum",
      "Frans Banning Cocq, heer van purmerlant en Ilpendam, Capiteijn Willem van Ruijtenburch van Vlaerdingen, heer van Vlaerdingen, Lu[ij]tenant, Jan Visscher Cornelisen Vaendrich, Rombout Kemp Sergeant, Reijnier Engelen Sergeant, Barent Harmansen, Jan Adriaensen Keyser, Elbert Willemsen, Jan Clasen Leydeckers, Jan Ockersen, Jan Pietersen bronchorst, Harman Iacobsen wormskerck, Jacob Dircksen de Roy, Jan vander heede, Walich Schellingwou, Jan brugman, Claes van Cruysbergen, Paulus Schoonhoven",
      "inscriptie"
    ],
    "documentation": [
      "Inzoomer object op zaal, 2013 (Nederlands/English).",
      "A. Jensen Adams, Public Faces and Private Identities in Seventeenth-Century Holland, Portraiture and the Production of Community, New York 2009, p. 211-217, fig. 60.",
      "M. Rayssac, 'l'Exode des Musées, Histoire des oeuvres d'art sous l'Occupation', Parijs 2007.",
      // more results...
    ],
    "catRefRPK": [],
    "principalOrFirstMaker": "Rembrandt Harmensz. van Rijn",
    "dimensions": [
      {
        "unit": "cm",
        "type": "hoogte",
        "part": null,
        "value": "379,5"
      },
      {
        "unit": "cm",
        "type": "breedte",
        "part": null,
        "value": "453,5"
      },
      {
        "unit": "kg",
        "type": "gewicht",
        "part": null,
        "value": "337"
      },
      {
        "unit": "kg",
        "type": "gewicht",
        "part": null,
        "value": "170"
      }
    ],
    "physicalProperties": [],
    "physicalMedium": "olieverf op doek",
    "longTitle": "Schutters van wijk II onder leiding van kapitein Frans Banninck Cocq, bekend als de ‘Nachtwacht’, Rembrandt Harmensz. van Rijn, 1642",
    "subTitle": "h 379,5cm × b 453,5cm × g 337kg",
    "scLabelLine": "Rembrandt Harmensz. van Rijn (15-jul-1606 - 08-okt-1669), 1642, olieverf op doek",
    "label": {
      "title": "Schutters van wijk II onder leiding van kapitein Frans Banninck Cocq, bekend als de ‘Nachtwacht’",
      "makerLine": "Rembrandt Harmensz van Rijn (1606–1669), olieverf op doek, 1642",
      "description": null,
      "notes": null,
      "date": "2013-05-22"
    },
    "showImage": true
  },
  "artObjectPage": {
    "id": "nl-sk-c-5",
    "similarPages": [],
    "lang": "nl",
    "objectNumber": "sk-c-5",
    "tags": [],
    "plaqueDescription": null,
    "audioFile1": null,
    "audioFileLabel1": null,
    "audioFileLabel2": null,
    "createdOn": "0001-01-01T00:00:00Z",
    "updatedOn": "0001-01-01T00:00:00Z",
    "adlibOverrides": {
      "titel": null,
      "maker": null,
      "etiketText": null
    }
  }
where I at this moment need only artObjects -> makers -> name and artobject -> title 

And I need to do that for all 10

But as far as I know I can only have one fromJson so how do I take care that the second one is called and parsed
and the right info is on the Painting object 

That is my question all the time 

Roelof

Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Ben Coman


On Mon, 11 Mar 2019 at 01:09, Roelof Wobben <[hidden email]> wrote:
Op 10-3-2019 om 17:45 schreef Ben Coman:


On Sun, 10 Mar 2019 at 23:40, Roelof Wobben <[hidden email]> wrote:
Op 10-3-2019 om 16:19 schreef Ben Coman:


On Sun, 10 Mar 2019 at 17:55, Roelof Wobben <[hidden email]> wrote:
I could do something like this :

getImages
    | json numbers json2  |
    json := NeoJSONReader
        fromString:
            (ZnEasy
                get:
                    'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True')
                contents.
    numbers := self class fromJSON: json
    numbers do: [each |  json :=  .........
                                           imageData = self class ???

I'm not sure what "numbers" refers to. It seems a quite non-domain related identifier.
To understand the domain, pasting the json contents of your link above into https://jsonformatter.curiousconcept.com/
and collapsing objects I see the structure is...
<snip> 

Did you work through this example I provided?   Please do so.
In a clean image...
Start with only your original code (which was good btw)...

    Paintings class >>  fromJSON: json
        | instance artObjects |
        instance := self new.
        artObjects := json at: #artObjects.
        artObjects
            do:
                [ :eachArtObject | instance addPainting: (Painting fromJSON: eachArtObject) ].
        ^ instance

except objectNumber(s) seem associated with each painting, so should be stored within the painting object, as ADDED here... 

 Painting class >> fromJSON: json
        | instance |
        instance := self new.
        instance
            objectNumber: (json at: #objectNumber);     "<<<<<<<<ADDED"
            title: (json at: #title);
            painter: (json at: #principalOrFirstMaker);
            imageUrl: ((json at: #webImage) at: #url).
        ^ instance

Evaluate the following in Playground... 
    collectionJson := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: collectionJson.
    paintings inspect.
then clean the DNU errors as they occur (i.e. click the <Create> button shown by the debugger, and fill in as follows...)  
when you save the methods, choose <Declare new instance variable>"  

    Painting >> objectNumber: aString 
objectNumber := aString  
    Painting >> title: aString 
title := aString     

    Painting >> painter: aString 
painter := aString

    Painting >> imageUrl: aString 
imageUrl := aString

    Paintings >> addPainting: aPainting 
paintings := paintings ifNil: [ OrderedCollection new ].
paintings add: aPainting.

and an inspector will appear showing a list of paintings, which you can drill down to a painting and see each has the expected data.
Now just to round things off...

    Painting >> title
^ title

    Painting >> printOn: aStream
super printOn: aStream.
aStream << ' (' << self title << ')'

to help distinguish each item in the Inspector.


To extend the Playground code to download and display a painting, in Playground evaluate...

    painting := paintings first.
    imageResponse := ZnEasy get: painting imageUrl.
    image := ImageReadWriter formFromStream: imageResponse entity readStream.
    image inspect.

    Paintings >> first
^ paintings first

    Painting >> imageUrl
^ imageUrl

and an inspector on the `image` variable displays the painting on the Morph tab.


Now to mold the IDE to your domain... 
using Spotter to browser gtInspector* methods, a promising find is... AbstractFileReference>>gitInspectorJpegIn:
from which I produced...

    Painting  >>  gtInspectorJpegIn: composite
<gtInspectorPresentationOrder: 0>
composite morph
title: 'Painting';
display: [ ImageReadWriter formFromStream: self imageEntity readStream ] 

Then inspecting the `paintings` variable and drilling down to a painting 
pops up a DNU #imageEntity, which can be resolved by...

    Painting >> imageEntity
^ imageEntity ifNil: [ imageEntity := (ZnEasy get: self imageUrl) entity ].

and you get to see the painting shown in the Inspector.


Now if I understand your question... "Do I need to make some more object to get this working..."
I'd say... No. You only want one object for each painting. Once you have a painting object, it should handle all getting all further data it needs for itself.
You don't want duplicate objects each having half of the data.


can I for example name the function fromJson2 or fromJSONFromLink2  ?

By naming convention #fromJson: implies it sits on the class side.
To get further data for an existing object you want an instance-side method, maybe named #getLink2Json. 

HTH, 
cheers -ben


oke

numbers should be the objectNumbers because for the rest I need them


 


I use a instance variable called painting to hold the data.


so I can change the call to get data for the painting to :

Painting class >> fromJSON: json
        | instance |
        instance := self new.
        instance
            objectNumber: ((json at: #objectNumber).
        ^ instance


so numbers should be a collection of 10 ObjectNumbers

I'm presuming that 10 objectNumbers relate to 10 different paintings,
in which case, No, you should never need to deal with a collection of 10 objectNumbers.
What you should have is a collection of painting-objects which know their own objectNumber
and then iterate that collection sending each painting-object the message "getLink2Json".

 
so I should use a loop to make a call to the second api call

In Pharo OO approach, you don't "loop" on the "calls", 
you "iterate" on the "objects" asking them to use their own data to do the stuff they know how to do.
i.e. Let each object take care of itself.

Which class is getImages defined on?  That is really important to understand your proposed solution. 
I'm just presuming its defined on the class side of Paintings since your are referring to the same URL as before.

 getImages
    | json numbers  |
    json := NeoJSONReader
        fromString:
            (ZnEasy
                get:
                    'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True')
                contents.
    numbers painting := self class fromJSON: json  

btw, it won't work with two identifiers "numbers painting" to the left of the assignment symbol.  Space here bad.
 

    numbers  do: [each |  json :=  call to the second url.here I used each 
                                           imageData = self class getLink2Json 
                                           json2 := call to the 3th url
                                           otherData := self class  getLink3Json
                                       

no :)    the "self class" seems wrong.
getting extra data about a painting should be handled by the instance side of its painting-object.

 
Then  I can  I hope on every json method fill in the data I need

Do I understand you well

I think not so well yet :)   (or else I am missing something)
Keep trying.  It will change the way you think about programming.

Please work though the example I provided.
cheers -ben

 

I did and it worked the same as my code
Still I do not see how to use then the objectNumber in a second call to the api
for example

lets say we have only 1 objectNumber now
for example
SK-C-5

then I need that one here :   
https://www.rijksmuseum.nl/api/nl/collection/SK-C-5?key=[API_KEY]&format=json

so I see a json response of this : 
<snip>
where I at this moment need only artObjects -> makers -> name and artobject -> title 
And I need to do that for all 10

But as far as I know I can only have one fromJson 
You can have as many Painting-class>>fromJsonXxxx methods as you like, 
but you only call one of them once per painting.  This has nothing to do with Json
and everything to do with your Painting-class>>fromJson method creating a new Painting-object, 
and you only want *one* Painting-object per Json-painting, so you can only call one.

But actually, you don't need any Painting>>fromJSON:.
Please delete it !!!!

Then modify the other one as follows...
    Paintings class >> fromJSON: json
    | instance |
    instance := self new.
    (json at: #artObjects)
        do:
            [ :artObjectJson | 
    |painting|
painting := Painting new.
    painting
        objectNumber: (artObjectJson  at: #objectNumber);
          title: (artObjectJson  at: #title);
        painter: (artObjectJson  at: #principalOrFirstMaker);
        imageUrl: ((artObjectJson  at: #webImage) at: #url).
instance addPainting: painting ].
    ^ instance
 
Does...
    json := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: json.

give you the same result as before?

so how do I take care that the second one is called and parsed
and the right info is on the Painting object 

That is my question all the time 

As I said before...
> What you should have is a collection of painting-objects which know their own objectNumber
> and then iterate that collection sending each painting-object the message "getLink2Json".

So more generically, add something like...
    Painting >> getMoreData  "or a better name of your own"
            | url json artObjectJson | 
            url := 'https://www.rijksmuseum.nl/api/nl/collection/' , objectNumber , '?key=[API_KEY]&format=json'
Transcript crShow: 'Getting description from ', url.
json  := (NeoJSONReader fromString: (ZnEasy get: url) contents).
artObjectJson := json at: 'artObject.
             description := artObjectJson at: 'description'.
             title := artObjectJson at: 'title'.

Now in playground, variable paintings holds a collection 10 ten paintings.
So lets ask each of them to get more data.

paintings do: [ :painting | painting getMoreData ].


cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Roelof
Op 11-3-2019 om 00:03 schreef Ben Coman:
> collectionUrl := 
> 'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True'.
>     json := NeoJSONReader fromString: (ZnEasy get: collectionUrl)
> contents.
>     paintings := Paintings fromJSON: json.

Thanks a lot , Ben

I almost gave it up but I think I see a way to solve this

The first part gives back exactly the same output as my old code.
For the new one I have to do some trying but I think I can solve it

Roelof


Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Ben Coman


On Mon, 11 Mar 2019 at 14:35, Roelof Wobben <[hidden email]> wrote:
Op 11-3-2019 om 00:03 schreef Ben Coman:
> collectionUrl := 
> 'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True'.
>     json := NeoJSONReader fromString: (ZnEasy get: collectionUrl)
> contents.
>     paintings := Paintings fromJSON: json.

Thanks a lot , Ben

I almost gave it up but I think I see a way to solve this

The first part gives back exactly the same output as my old code.
For the new one I have to do some trying but I think I can solve it

great !! 
Now it occurred to me that your approach seemed to be "based from applying functions to manipulate data that happens to be stored in an object",
whereas you should invert that philosophy so you are "thinking from inside the object".  What data do "I" have and what can "I" do with it.  

A great read that will help you is "A Mentoring Course in Smalltalk" 

cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Roelof
In reply to this post by Ben Coman
Op 11-3-2019 om 00:03 schreef Ben Coman:


On Mon, 11 Mar 2019 at 01:09, Roelof Wobben <[hidden email]> wrote:
Op 10-3-2019 om 17:45 schreef Ben Coman:


On Sun, 10 Mar 2019 at 23:40, Roelof Wobben <[hidden email]> wrote:
Op 10-3-2019 om 16:19 schreef Ben Coman:


On Sun, 10 Mar 2019 at 17:55, Roelof Wobben <[hidden email]> wrote:
I could do something like this :

getImages
    | json numbers json2  |
    json := NeoJSONReader
        fromString:
            (ZnEasy
                get:
                    'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True')
                contents.
    numbers := self class fromJSON: json
    numbers do: [each |  json :=  .........
                                           imageData = self class ???

I'm not sure what "numbers" refers to. It seems a quite non-domain related identifier.
To understand the domain, pasting the json contents of your link above into https://jsonformatter.curiousconcept.com/
and collapsing objects I see the structure is...
<snip> 

Did you work through this example I provided?   Please do so.
In a clean image...
Start with only your original code (which was good btw)...

    Paintings class >>  fromJSON: json
        | instance artObjects |
        instance := self new.
        artObjects := json at: #artObjects.
        artObjects
            do:
                [ :eachArtObject | instance addPainting: (Painting fromJSON: eachArtObject) ].
        ^ instance

except objectNumber(s) seem associated with each painting, so should be stored within the painting object, as ADDED here... 

 Painting class >> fromJSON: json
        | instance |
        instance := self new.
        instance
            objectNumber: (json at: #objectNumber);     "<<<<<<<<ADDED"
            title: (json at: #title);
            painter: (json at: #principalOrFirstMaker);
            imageUrl: ((json at: #webImage) at: #url).
        ^ instance

Evaluate the following in Playground... 
    collectionJson := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: collectionJson.
    paintings inspect.
then clean the DNU errors as they occur (i.e. click the <Create> button shown by the debugger, and fill in as follows...)  
when you save the methods, choose <Declare new instance variable>"  

    Painting >> objectNumber: aString 
objectNumber := aString  
    Painting >> title: aString 
title := aString     

    Painting >> painter: aString 
painter := aString

    Painting >> imageUrl: aString 
imageUrl := aString

    Paintings >> addPainting: aPainting 
paintings := paintings ifNil: [ OrderedCollection new ].
paintings add: aPainting.

and an inspector will appear showing a list of paintings, which you can drill down to a painting and see each has the expected data.
Now just to round things off...

    Painting >> title
^ title

    Painting >> printOn: aStream
super printOn: aStream.
aStream << ' (' << self title << ')'

to help distinguish each item in the Inspector.


To extend the Playground code to download and display a painting, in Playground evaluate...

    painting := paintings first.
    imageResponse := ZnEasy get: painting imageUrl.
    image := ImageReadWriter formFromStream: imageResponse entity readStream.
    image inspect.

    Paintings >> first
^ paintings first

    Painting >> imageUrl
^ imageUrl

and an inspector on the `image` variable displays the painting on the Morph tab.


Now to mold the IDE to your domain... 
using Spotter to browser gtInspector* methods, a promising find is... AbstractFileReference>>gitInspectorJpegIn:
from which I produced...

    Painting  >>  gtInspectorJpegIn: composite
<gtInspectorPresentationOrder: 0>
composite morph
title: 'Painting';
display: [ ImageReadWriter formFromStream: self imageEntity readStream ] 

Then inspecting the `paintings` variable and drilling down to a painting 
pops up a DNU #imageEntity, which can be resolved by...

    Painting >> imageEntity
^ imageEntity ifNil: [ imageEntity := (ZnEasy get: self imageUrl) entity ].

and you get to see the painting shown in the Inspector.


Now if I understand your question... "Do I need to make some more object to get this working..."
I'd say... No. You only want one object for each painting. Once you have a painting object, it should handle all getting all further data it needs for itself.
You don't want duplicate objects each having half of the data.


can I for example name the function fromJson2 or fromJSONFromLink2  ?

By naming convention #fromJson: implies it sits on the class side.
To get further data for an existing object you want an instance-side method, maybe named #getLink2Json. 

HTH, 
cheers -ben


oke

numbers should be the objectNumbers because for the rest I need them


 


I use a instance variable called painting to hold the data.


so I can change the call to get data for the painting to :

Painting class >> fromJSON: json
        | instance |
        instance := self new.
        instance
            objectNumber: ((json at: #objectNumber).
        ^ instance


so numbers should be a collection of 10 ObjectNumbers

I'm presuming that 10 objectNumbers relate to 10 different paintings,
in which case, No, you should never need to deal with a collection of 10 objectNumbers.
What you should have is a collection of painting-objects which know their own objectNumber
and then iterate that collection sending each painting-object the message "getLink2Json".

 
so I should use a loop to make a call to the second api call

In Pharo OO approach, you don't "loop" on the "calls", 
you "iterate" on the "objects" asking them to use their own data to do the stuff they know how to do.
i.e. Let each object take care of itself.

Which class is getImages defined on?  That is really important to understand your proposed solution. 
I'm just presuming its defined on the class side of Paintings since your are referring to the same URL as before.

 getImages
    | json numbers  |
    json := NeoJSONReader
        fromString:
            (ZnEasy
                get:
                    'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True')
                contents.
    numbers painting := self class fromJSON: json  

btw, it won't work with two identifiers "numbers painting" to the left of the assignment symbol.  Space here bad.
 

    numbers  do: [each |  json :=  call to the second url.here I used each 
                                           imageData = self class getLink2Json 
                                           json2 := call to the 3th url
                                           otherData := self class  getLink3Json
                                       

no :)    the "self class" seems wrong.
getting extra data about a painting should be handled by the instance side of its painting-object.

 
Then  I can  I hope on every json method fill in the data I need

Do I understand you well

I think not so well yet :)   (or else I am missing something)
Keep trying.  It will change the way you think about programming.

Please work though the example I provided.
cheers -ben

 

I did and it worked the same as my code
Still I do not see how to use then the objectNumber in a second call to the api
for example

lets say we have only 1 objectNumber now
for example
SK-C-5

then I need that one here :   
https://www.rijksmuseum.nl/api/nl/collection/SK-C-5?key=[API_KEY]&format=json

so I see a json response of this : 
<snip>
where I at this moment need only artObjects -> makers -> name and artobject -> title 
And I need to do that for all 10

But as far as I know I can only have one fromJson 
You can have as many Painting-class>>fromJsonXxxx methods as you like, 
but you only call one of them once per painting.  This has nothing to do with Json
and everything to do with your Painting-class>>fromJson method creating a new Painting-object, 
and you only want *one* Painting-object per Json-painting, so you can only call one.

But actually, you don't need any Painting>>fromJSON:.
Please delete it !!!!

Then modify the other one as follows...
    Paintings class >> fromJSON: json
    | instance |
    instance := self new.
    (json at: #artObjects)
        do:
            [ :artObjectJson | 
    |painting|
painting := Painting new.
    painting
        objectNumber: (artObjectJson  at: #objectNumber);
          title: (artObjectJson  at: #title);
        painter: (artObjectJson  at: #principalOrFirstMaker);
        imageUrl: ((artObjectJson  at: #webImage) at: #url).
instance addPainting: painting ].
    ^ instance
 
Does...
    json := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: json.

give you the same result as before?

so how do I take care that the second one is called and parsed
and the right info is on the Painting object 

That is my question all the time 

As I said before...
> What you should have is a collection of painting-objects which know their own objectNumber
> and then iterate that collection sending each painting-object the message "getLink2Json".

So more generically, add something like...
    Painting >> getMoreData  "or a better name of your own"
            | url json artObjectJson | 
            url := 'https://www.rijksmuseum.nl/api/nl/collection/' , objectNumber , '?key=[API_KEY]&format=json'
Transcript crShow: 'Getting description from ', url.
json  := (NeoJSONReader fromString: (ZnEasy get: url) contents).
artObjectJson := json at: 'artObject.
             description := artObjectJson at: 'description'.
             title := artObjectJson at: 'title'.
Now in playground, variable paintings holds a collection 10 ten paintings.
So lets ask each of them to get more data.
paintings do: [ :painting | painting getMoreData ].
cheers -ben


I did try to make this idea work but now im complete lost.

Right now the collection that should contain the objectNumbers is total empty
anyone who can see where I went wrong.

I included my code so far.

Roelof



Paintings.st (9K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Ben Coman
On Wed, 13 Mar 2019 at 00:43, Roelof Wobben <[hidden email]> wrote:
> I did try to make this idea work but now im complete lost.
>
> Right now the collection that should contain the objectNumbers is total empty
> anyone who can see where I went wrong.
>
> I included my code so far.


A few things...
!Paintings class methodsFor: 'instance creation' stamp: 'RoelofWobben 3/12/2019 17:26'!
getImagesDataFromJSON: json
| instance |
instance := Painting new.
(json at: #values)
do: [ :artObjectJson | 
| painting |
painting := Painting new.
painting imageUrl: (json at: #name).
instance addPainting: painting ].
^ instance! !

You are adding a painting to a painting, which semantically makes no sense.
i.e. where #addPainting: is sent to "instance" , that is holding a Painting.

I would not expect your Painting class to have a method addPainting:
Paintings should have that method.

btw, Stop using "instance" as a variable name.  
Its as bad as variables named "a", "i", "t", "z" - it provides no semantic context.

--------------------
Object subclass: #Paintings
instanceVariableNames: 'paintings numbers'

Paintings class
instanceVariableNames: 'numbers paintings'!

You've defined these variables on both the instance-side and class-side.
I'll hazard a guess that you should delete the class-side definition. 

----------------------------------

TBApplicationRootComponent initialize!Object subclass: #Painting
instanceVariableNames: 'imageUrl'

Why are you *not* storing objectNumber inside each Painting?
What is the relationship between paintings and objectNumbers?
Is it one-to-many or one-to-one?

-------------------
!Paintings methodsFor: 'instance creation' stamp: 'RoelofWobben 3/11/2019 08:05'!
initialize
super initialize.
numbers := OrderedCollection new! !

Why do you *not* initialize variable "paintings" to "OrderedCollection new" ?
Aren't you dealing with a collection of paintings?

----------------
There is lots more confusing me here, so before going further,
in a fresh new image, can you load NeoJSON and the attached fileout.

Then in Playground do...
    json := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: json.
    paintings inspect.

and then debug trace through that whole thing.
Is that what you were trying to do?  Or have I got the wrong end of the stick?

cheers -ben

Rijksmuseum.st (3K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Roelof
Op 12-3-2019 om 18:46 schreef Ben Coman:
On Wed, 13 Mar 2019 at 00:43, Roelof Wobben <[hidden email]> wrote:
> I did try to make this idea work but now im complete lost.
>
> Right now the collection that should contain the objectNumbers is total empty
> anyone who can see where I went wrong.
>
> I included my code so far.


A few things...
!Paintings class methodsFor: 'instance creation' stamp: 'RoelofWobben 3/12/2019 17:26'!
getImagesDataFromJSON: json
| instance |
instance := Painting new.
(json at: #values)
do: [ :artObjectJson | 
| painting |
painting := Painting new.
painting imageUrl: (json at: #name).
instance addPainting: painting ].
^ instance! !

You are adding a painting to a painting, which semantically makes no sense.
i.e. where #addPainting: is sent to "instance" , that is holding a Painting.

I would not expect your Painting class to have a method addPainting:
Paintings should have that method.

btw, Stop using "instance" as a variable name.  
Its as bad as variables named "a", "i", "t", "z" - it provides no semantic context.

--------------------
Object subclass: #Paintings
instanceVariableNames: 'paintings numbers'

Paintings class
instanceVariableNames: 'numbers paintings'!

You've defined these variables on both the instance-side and class-side.
I'll hazard a guess that you should delete the class-side definition. 

----------------------------------

TBApplicationRootComponent initialize!Object subclass: #Painting
instanceVariableNames: 'imageUrl'

Why are you *not* storing objectNumber inside each Painting?
What is the relationship between paintings and objectNumbers?
Is it one-to-many or one-to-one?

-------------------
!Paintings methodsFor: 'instance creation' stamp: 'RoelofWobben 3/11/2019 08:05'!
initialize
super initialize.
numbers := OrderedCollection new! !

Why do you *not* initialize variable "paintings" to "OrderedCollection new" ?
Aren't you dealing with a collection of paintings?

----------------
There is lots more confusing me here, so before going further,
in a fresh new image, can you load NeoJSON and the attached fileout.

Then in Playground do...
    json := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: json.
    paintings inspect.

and then debug trace through that whole thing.
Is that what you were trying to do?  Or have I got the wrong end of the stick?

cheers -ben


Oke, I try to tell what I want

Right now I only want to fetch the objectNumber is a collection.

so I can use that in a later step.

There is a one to one relationship between the objectNumber and a Painting

I do not use the imageUrl found on this url because it's to big .

So what I try now is first put all the objectNumber in a collection
and use that collection on a second url to fetch a image of the right size

I hope im clear what I try to do in this step

so numbers should be a collection of objectNumbers and paintings a collection of painting with the info I need

so to recap

this one must give me a collection of objectNumbers : 'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True'.

and the objectNumbers I can use for this url : 
https://www.rijksmuseum.nl/api/nl/collection/<one objectNumber/tiles?key=14OGzuak&format=json

and I do not store the objectNumber in Paintings because I do not use it to display it in a seaside project. 

Roelof

Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Roelof
In reply to this post by Ben Coman
Op 11-3-2019 om 00:03 schreef Ben Coman:
getMoreData  "or a better name of your own"
            | url json artObjectJson | 
            url := 'https://www.rijksmuseum.nl/api/nl/collection/' , objectNumber , '?key=[API_KEY]&format=json'
Transcript crShow: 'Getting description from ', url.
json  := (NeoJSONReader fromString: (ZnEasy get: url) contents).
artObjectJson := json at: 'artObject.
             description := artObjectJson at: 'description'.
             title := artObjectJson at: 'title'.


hello Ben,

I think I almost see what you mean but still one problem

when doing this code :

 collectionUrl :=  'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True'.
    json := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: json.
    paintings do: [:painting | painting getMoreData  ]

here I see a error : instance of Paintings do not understand do:

I think we have to get the directory named paintings out of the object,


this seems to be working :

 collectionUrl :=  'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True'.
    json := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: json.
    (paintings paintings)  do: [:painting | painting getMoreData  ]

Roelof

Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Ben Coman
In reply to this post by Roelof


On Wed, 13 Mar 2019 at 02:10, Roelof Wobben <[hidden email]> wrote:
Op 12-3-2019 om 18:46 schreef Ben Coman:
Then in Playground do...
    json := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: json.
    paintings inspect.

and then debug trace through that whole thing.
Is that what you were trying to do?  Or have I got the wrong end of the stick?

cheers -ben

There is a one to one relationship between the objectNumber and a Painting

I do not use the imageUrl found on this url because it's to big .

okay. So I presume you want "artObject/webImage/url" ?

 

So what I try now is first put all the objectNumber in a collection
and use that collection on a second url to fetch a image of the right size

Okay. I understand, but I am *still* coaching you to *not* do that.
Rather than gathering a collection of dumb numbers and then creating new Painting objects from those,
(which is awkward for joining the new data with the previous data)
work with the Painting objects you already have. 
Add a new instance variables "webImageUrl" and "webImage"

Then either
        Painting >> getMoreData
             |entity|
              ...
              webImageUrl := (artObjectJson at: 'webImage') at: 'url'.
              entity :=  (ZnEasy get: webImageUrl) entity.
              webImage := ImageReadWriter formFromStream: entity readStream.

which may lock the UI for a moment downloading them all,
or download lazily (slight variation on my previous imageEntity which doubles up on storage)...

    Painting >> getMoreData
           ...
           webImageUrl := (artObjectJson at: 'webImage') at: 'url'.


    Painting >> webImage
        |entity|
        webImageUrl ifNil: [^nil].
        webImage ifNil: [
              entity :=  (ZnEasy get: webImageUrl) entity.
              webImage := ImageReadWriter formFromStream: entity readStream].
       ^ webImage

    Painting >> gtInspectorJpegIn: composite
<gtInspectorPresentationOrder: 0>
composite morph
title: 'Painting';
display: [ self webImage ] 


I hope im clear what I try to do in this step

yes it is.  Please take my coaching and kill your collection of numbers. 
Keeping them keeps you stuck in old ways of thinking. So just kill it :)
Once you've done that, alternatives will open up for you
and you'll be on the path to a new way of thinking about programming. :) :)
 
so numbers should be a collection of objectNumbers

no.  
 
and paintings a collection of painting with the info I need

yes, and objectNumber is part of the info of a painting.
 

so to recap

this one must give me a collection of objectNumbers : 'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True'.

no. it gives you a collection of paintings, of which objectNumber is one attribute.

 

and the objectNumbers I can use for this url : 
https://www.rijksmuseum.nl/api/nl/collection/<one objectNumber/tiles?key=14OGzuak&format=json

and I do not store the objectNumber in Paintings because I do not use it to display it in a seaside project. 

Regardless of whether you display it or not, objectNumber is supplied by the server "as an attribute belonging to painting"
so store it inside your Painting objects.  

Then rather than Seaside asking a collection of dumb numbers for each JPEG to display
it asks a collection of smart Painting objects for their JPEG to display (since they know their own objectNumber, they can get the JPEG themselves).

cheers -ben




On Wed, 13 Mar 2019 at 02:10, Roelof Wobben <[hidden email]> wrote:
Op 12-3-2019 om 18:46 schreef Ben Coman:
On Wed, 13 Mar 2019 at 00:43, Roelof Wobben <[hidden email]> wrote:
> I did try to make this idea work but now im complete lost.
>
> Right now the collection that should contain the objectNumbers is total empty
> anyone who can see where I went wrong.
>
> I included my code so far.


A few things...
!Paintings class methodsFor: 'instance creation' stamp: 'RoelofWobben 3/12/2019 17:26'!
getImagesDataFromJSON: json
| instance |
instance := Painting new.
(json at: #values)
do: [ :artObjectJson | 
| painting |
painting := Painting new.
painting imageUrl: (json at: #name).
instance addPainting: painting ].
^ instance! !

You are adding a painting to a painting, which semantically makes no sense.
i.e. where #addPainting: is sent to "instance" , that is holding a Painting.

I would not expect your Painting class to have a method addPainting:
Paintings should have that method.

btw, Stop using "instance" as a variable name.  
Its as bad as variables named "a", "i", "t", "z" - it provides no semantic context.

--------------------
Object subclass: #Paintings
instanceVariableNames: 'paintings numbers'

Paintings class
instanceVariableNames: 'numbers paintings'!

You've defined these variables on both the instance-side and class-side.
I'll hazard a guess that you should delete the class-side definition. 

----------------------------------

TBApplicationRootComponent initialize!Object subclass: #Painting
instanceVariableNames: 'imageUrl'

Why are you *not* storing objectNumber inside each Painting?
What is the relationship between paintings and objectNumbers?
Is it one-to-many or one-to-one?

-------------------
!Paintings methodsFor: 'instance creation' stamp: 'RoelofWobben 3/11/2019 08:05'!
initialize
super initialize.
numbers := OrderedCollection new! !

Why do you *not* initialize variable "paintings" to "OrderedCollection new" ?
Aren't you dealing with a collection of paintings?

----------------
There is lots more confusing me here, so before going further,
in a fresh new image, can you load NeoJSON and the attached fileout.

Then in Playground do...
    json := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: json.
    paintings inspect.

and then debug trace through that whole thing.
Is that what you were trying to do?  Or have I got the wrong end of the stick?

cheers -ben


Oke, I try to tell what I want

Right now I only want to fetch the objectNumber is a collection.

so I can use that in a later step.

There is a one to one relationship between the objectNumber and a Painting

I do not use the imageUrl found on this url because it's to big .

So what I try now is first put all the objectNumber in a collection
and use that collection on a second url to fetch a image of the right size

I hope im clear what I try to do in this step

so numbers should be a collection of objectNumbers and paintings a collection of painting with the info I need

so to recap

this one must give me a collection of objectNumbers : 'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True'.

and the objectNumbers I can use for this url : 
https://www.rijksmuseum.nl/api/nl/collection/<one objectNumber/tiles?key=14OGzuak&format=json

and I do not store the objectNumber in Paintings because I do not use it to display it in a seaside project. 

Roelof

Reply | Threaded
Open this post in threaded view
|

Re: complex json parsing.

Ben Coman
In reply to this post by Roelof


On Wed, 13 Mar 2019 at 04:59, Roelof Wobben <[hidden email]> wrote:
Op 11-3-2019 om 00:03 schreef Ben Coman:
getMoreData  "or a better name of your own"
            | url json artObjectJson | 
            url := 'https://www.rijksmuseum.nl/api/nl/collection/' , objectNumber , '?key=[API_KEY]&format=json'
Transcript crShow: 'Getting description from ', url.
json  := (NeoJSONReader fromString: (ZnEasy get: url) contents).
artObjectJson := json at: 'artObject.
             description := artObjectJson at: 'description'.
             title := artObjectJson at: 'title'.


hello Ben,

I think I almost see what you mean but still one problem

when doing this code :

 collectionUrl :=  'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True'.
    json := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: json.
    paintings do: [:painting | painting getMoreData  ]

here I see a error : instance of Paintings do not understand do:

I think we have to get the directory named paintings out of the object,


this seems to be working :

 collectionUrl :=  'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True'.
    json := NeoJSONReader fromString: (ZnEasy get: collectionUrl) contents.
    paintings := Paintings fromJSON: json.
    (paintings paintings)  do: [:painting | painting getMoreData  ]

You are right.  An alternative fix is...
    Paintings >> do: aBlock
         ^ paintings do: aBlock 

cheers -ben