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 :
|
On Sun, 10 Mar 2019 at 04:55, Roelof Wobben <[hidden email]> wrote:
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 |
Op 10-3-2019 om 02:13 schreef Ben
Coman:
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 |
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:
|
On Sun, 10 Mar 2019 at 17:55, Roelof Wobben <[hidden email]> wrote:
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... collectionUrl := 'https://www.rijksmuseum.nl/api/nl/collection?key=14OGzuak&format=json&type=schilderij&toppieces=True'. 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 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.
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 |
Op 10-3-2019 om 16:19 schreef Ben
Coman:
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 |
On Sun, 10 Mar 2019 at 23:40, Roelof Wobben <[hidden email]> wrote:
<snip> Did you work through this example I provided? Please do so. In a clean image...
except objectNumber(s) seem associated with each painting, so should be stored within the painting object, as ADDED here...
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
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".
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.
btw, it won't work with two identifiers "numbers painting" to the left of the assignment symbol. Space here bad.
no :) the "self class" seems wrong. getting extra data about a painting should be handled by the instance side of its painting-object.
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 |
In reply to this post by Ben Coman
On Sun, 10 Mar 2019 at 23:19, Ben Coman <[hidden email]> wrote:
@all, can someone suggest a better #display: block that would shrink the image to the window size, and/or zooming. cheers -ben |
In reply to this post by Ben Coman
Op 10-3-2019 om 17:45 schreef Ben
Coman:
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
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 |
On Mon, 11 Mar 2019 at 01:09, Roelof Wobben <[hidden email]> wrote:
<snip>
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... 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. give you the same result as before?
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 |
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 |
On Mon, 11 Mar 2019 at 14:35, Roelof Wobben <[hidden email]> wrote: Op 11-3-2019 om 00:03 schreef Ben Coman: 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 |
In reply to this post by Ben Coman
Op 11-3-2019 om 00:03 schreef Ben
Coman:
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 |
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... 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 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 |
Op 12-3-2019 om 18:46 schreef Ben
Coman:
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 :
|
In reply to this post by Ben Coman
Op 11-3-2019 om 00:03 schreef Ben
Coman:
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 |
In reply to this post by Roelof
On Wed, 13 Mar 2019 at 02:10, Roelof Wobben <[hidden email]> wrote:
okay. So I presume you want "artObject/webImage/url" ? { "artObject": { "webImage": {
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 ]
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. :) :)
no.
yes, and objectNumber is part of the info of a painting.
no. it gives you a collection of paintings, of which objectNumber is one attribute.
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:
|
In reply to this post by Roelof
On Wed, 13 Mar 2019 at 04:59, Roelof Wobben <[hidden email]> wrote:
You are right. An alternative fix is... Paintings >> do: aBlock ^ paintings do: aBlock cheers -ben |
Free forum by Nabble | Edit this page |