I'm just about to write my first client interface to a REST service.
In some respects I understand this is as simple as doing GET responses using Zinc, but I'm inquiring about tutorials or libraries that might help. Most of the stuff turned up by searches is about server-side of REST. cheers -ben |
Hi,
yes, is about that. GET/POST/PUT/DELETE all have sense.
I’m currently playing with my latest fun project pharo-mastodon, http://github.com/estebanlm/pharo-mastodon (the long term project is “getting out twitter” :P). It is actually a rest api… and since is very young, is not complicated at all, so you can take it as an example (probably of "things not to do”, but well… ;) ) Esteban
|
With ZnClient you can also dynamically extend paths (#addPath:), specify parameters (#formAt:put:, #queryAt:put:), provide raw #contents:, etc. But what would be nice is to convert the responses to some domain objects. So e.g. when I query trello for all lists, I would like to get as a result not json, but List instances, where if I asked for cards, it would query the cards from the server. I recommend also looking at this project by Richard Prinz https://www.min.at/prinz/?x=entry:entry150318-104537 which basically discovers the API and generates a nice implementation for it. Then you use the generated API instead of worrying about constructing HTTP requests. Peter On Tue, Dec 5, 2017 at 12:59 PM, Esteban Lorenzano <[hidden email]> wrote:
|
In reply to this post by Ben Coman
Ben,
> On 5 Dec 2017, at 12:15, Ben Coman <[hidden email]> wrote: > > I'm just about to write my first client interface to a REST service. > In some respects I understand this is as simple as doing GET responses > using Zinc, > but I'm inquiring about tutorials or libraries that might help. Most > of the stuff turned up by searches is about server-side of REST. > > cheers -ben I am not sure what you are looking for, but it is not difficult to do. ZnClient is your go to class. The Enterprise Pharo book chapters about HTTP client & server are starting points (in particular section 11 of the former, and very specifically 11.3). HTH, Sven |
@esteban, thx, I'll take a look at mastodon.
@peter, I remember playing with the Google API when Richard published that. thx for the reminder. @sven, I started reading Enterprise Pharo a couple of hours ago. I don't quite get your section references. I presume you don't mean "chapter 11 Persisting Objects with Voyage, 11.3 Enhancing Storage" That seems off topic. And "chapter 4 Zinc HTTP: The Client Side, 4.11 Headers" doesn't have a sub-part "3". I guess part of what I'm interested in are patterns for hooking NeoJSON up to parse a REST response into objects to build a wrapper around a REST service. I see a chapter in Enterprise Pharo, which I'll get to that soon. Perhaps I was premature asking before reading that, but its good to have a few paths to explore. btw, here is the concrete case... https://bittrex.com/home/api cheers -ben On 5 December 2017 at 20:06, Sven Van Caekenberghe <[hidden email]> wrote: > Ben, > >> On 5 Dec 2017, at 12:15, Ben Coman <[hidden email]> wrote: >> >> I'm just about to write my first client interface to a REST service. >> In some respects I understand this is as simple as doing GET responses >> using Zinc, >> but I'm inquiring about tutorials or libraries that might help. Most >> of the stuff turned up by searches is about server-side of REST. >> >> cheers -ben > > I am not sure what you are looking for, but it is not difficult to do. ZnClient is your go to class. > > The Enterprise Pharo book chapters about HTTP client & server are starting points (in particular section 11 of the former, and very specifically 11.3). > > HTH, > > Sven > > > |
In reply to this post by Peter Uhnak
There is also the OpenAPI specification dedicated to define a REST API and from which a skeleton of an implementation can be generated. It should be then also possible to generate the skeleton of a client for such an API. Swagger provides tooling to the OpenAPI specification for several programming languages. Unfortunately, it seams there is no implementation for Smalltalk. Miguel Le 05/12/2017 à 13:05, Peter Uhnák a
écrit :
|
In reply to this post by Ben Coman
> On 5 Dec 2017, at 13:33, Ben Coman <[hidden email]> wrote: > > @esteban, thx, I'll take a look at mastodon. > > @peter, I remember playing with the Google API when Richard published > that. thx for the reminder. > > @sven, I started reading Enterprise Pharo a couple of hours ago. > I don't quite get your section references. I presume you > don't mean "chapter 11 Persisting Objects with Voyage, 11.3 Enhancing Storage" > That seems off topic. And "chapter 4 Zinc HTTP: The Client Side, 4.11 Headers" > doesn't have a sub-part "3". I meant 11.3 in this page https://ci.inria.fr/pharo-contribution/job/EnterprisePharoBook/lastSuccessfulBuild/artifact/book-result/Zinc-HTTP-Server/Zinc-HTTP-Server.html a section called 11.3. A Zinc Client. > I guess part of what I'm interested in are patterns for hooking > NeoJSON up to parse a REST response into objects to build a wrapper > around a REST service. I see a chapter in Enterprise Pharo, which I'll > get to that soon. Perhaps I was premature asking before reading that, > but its good to have a few paths to explore. Here is a recent example http://forum.world.st/Another-example-of-invoking-a-REST-JSON-web-service-resolving-User-Agent-strings-tt5017489.html > btw, here is the concrete case... > https://bittrex.com/home/api > > cheers -ben > > On 5 December 2017 at 20:06, Sven Van Caekenberghe <[hidden email]> wrote: >> Ben, >> >>> On 5 Dec 2017, at 12:15, Ben Coman <[hidden email]> wrote: >>> >>> I'm just about to write my first client interface to a REST service. >>> In some respects I understand this is as simple as doing GET responses >>> using Zinc, >>> but I'm inquiring about tutorials or libraries that might help. Most >>> of the stuff turned up by searches is about server-side of REST. >>> >>> cheers -ben >> >> I am not sure what you are looking for, but it is not difficult to do. ZnClient is your go to class. >> >> The Enterprise Pharo book chapters about HTTP client & server are starting points (in particular section 11 of the former, and very specifically 11.3). >> >> HTH, >> >> Sven >> >> >> > |
On 5 December 2017 at 20:44, Sven Van Caekenberghe <[hidden email]> wrote:
> > >> On 5 Dec 2017, at 13:33, Ben Coman <[hidden email]> wrote: >> >> @esteban, thx, I'll take a look at mastodon. >> >> @peter, I remember playing with the Google API when Richard published >> that. thx for the reminder. >> >> @sven, I started reading Enterprise Pharo a couple of hours ago. >> I don't quite get your section references. I presume you >> don't mean "chapter 11 Persisting Objects with Voyage, 11.3 Enhancing Storage" >> That seems off topic. And "chapter 4 Zinc HTTP: The Client Side, 4.11 Headers" >> doesn't have a sub-part "3". > > I meant 11.3 in this page https://ci.inria.fr/pharo-contribution/job/EnterprisePharoBook/lastSuccessfulBuild/artifact/book-result/Zinc-HTTP-Server/Zinc-HTTP-Server.html a section called 11.3. A Zinc Client. > > >> I guess part of what I'm interested in are patterns for hooking >> NeoJSON up to parse a REST response into objects to build a wrapper >> around a REST service. I see a chapter in Enterprise Pharo, which I'll >> get to that soon. Perhaps I was premature asking before reading that, >> but its good to have a few paths to explore. > > Here is a recent example http://forum.world.st/Another-example-of-invoking-a-REST-JSON-web-service-resolving-User-Agent-strings-tt5017489.html > thx. those are both the examples I was seeking. btw, note the different numbering here... http://files.pharo.org/books-pdfs/entreprise-pharo/2016-10-06-EnterprisePharo.pdf "A Zinc client" is in section 5.11 with part "3" unnumbered cheers -ben >> btw, here is the concrete case... >> https://bittrex.com/home/api >> >> cheers -ben >> >> On 5 December 2017 at 20:06, Sven Van Caekenberghe <[hidden email]> wrote: >>> Ben, >>> >>>> On 5 Dec 2017, at 12:15, Ben Coman <[hidden email]> wrote: >>>> >>>> I'm just about to write my first client interface to a REST service. >>>> In some respects I understand this is as simple as doing GET responses >>>> using Zinc, >>>> but I'm inquiring about tutorials or libraries that might help. Most >>>> of the stuff turned up by searches is about server-side of REST. >>>> >>>> cheers -ben >>> >>> I am not sure what you are looking for, but it is not difficult to do. ZnClient is your go to class. >>> >>> The Enterprise Pharo book chapters about HTTP client & server are starting points (in particular section 11 of the former, and very specifically 11.3). >>> >>> HTH, >>> >>> Sven >>> >>> >>> >> > > |
In reply to this post by Ben Coman
Hi Ben,
I've made a few REST Clients http://smalltalkhub.com/#!/~pdebruic/Stripe http://smalltalkhub.com/#!/~pdebruic/Tropo http://smalltalkhub.com/#!/~pdebruic/SegmentIO And the elasticsearch one but its been advanced mostly lately by Sho Yoshida (https://github.com/newapplesho) here https://github.com/newapplesho/elasticsearch-smalltalk He has also made REST clients for Twilio, AWS, SendGrid, Salesforce, and Mixpanel among others. Norbert Hartl made one for Mandrill (Mailchimps transactional email service) http://smalltalkhub.com/#!/~NorbertHartl/Mandrill And Francois Stephany made one for Postmark (another transactional email service) http://smalltalkhub.com/#!/~PharoExtras/Postmark So those are some examples of different approaches. I also started/made a cross platform web client wrapper (just wraps calls to ZnClient or WebClient on Squeak) http://smalltalkhub.com/#!/~pdebruic/HTTPAPIClient But IIRC I only used it in the Stripe API client. Hope this gives you some ideas about how to approach your own solution Paul Ben Coman wrote > I'm just about to write my first client interface to a REST service. > In some respects I understand this is as simple as doing GET responses > using Zinc, > but I'm inquiring about tutorials or libraries that might help. Most > of the stuff turned up by searches is about server-side of REST. > > cheers -ben -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html |
Thanks Paul. It'll be good to review some concrete implementations,
and nice to see what services other other people find useful. I got a momentary urge the create a Pharo Distribution or catalog entry called something like "SamplRest" with a GUI to explore available REST data sources. Marketing angle for Pharo would be helping less technical people get a "live" hold of data from these sources, and then incrementally script against those live objects. I guess a bit like SegmentIO but on your own desktop rather than through a third party. (but I've not got the time right now) cheers -ben On 6 December 2017 at 02:46, Paul DeBruicker <[hidden email]> wrote: Hi Ben, |
In reply to this post by Sven Van Caekenberghe-2
On 5 December 2017 at 20:44, Sven Van Caekenberghe <[hidden email]> wrote:
Thanks Sven. That helped a lot. I'd like to report success. It may be useful to others to see how to progressively build up to parsing a Nested JSON REST 1. First parse the JSON into simple Dictionaries... (ZnClient new enforceHttpSuccess: true; accept: ZnMimeType applicationJson; contentReader: [ :entity | NeoJSONReader fromString: entity contents ]; get) inspect. ==>Dictionary( 'success' ==> true 'message' ==> '' 'result ' ==> an Array(a Dictionary('BaseCurrency'->'BTC' 'BaseCurrencyLong'->'Bitcoin') ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum') 2. Then parse the first level response into a real object... Object subclass: #BittrexResponse instanceVariableNames: 'success message result' classVariableNames: '' package: 'Bittrex' (ZnClient new enforceHttpSuccess: true; accept: ZnMimeType applicationJson; contentReader: [ :entity | (NeoJSONReader on: entity readStream) mapInstVarsFor: BittrexResponse ; nextAs: BittrexResponse ]; get) inspect. ==>BittrexResponse success => true message => '' result => an Array(a Dictionary('BaseCurrency'->'BTC' 'BaseCurrencyLong'->'Bitcoin') ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum') Or alternatively... (ZnClient new enforceHttpSuccess: true; accept: ZnMimeType applicationJson; contentReader: [ :entity | |reader| reader := (NeoJSONReader on: entity readStream). reader for: BittrexResponse do: [:m| m mapInstVar: #success. m mapInstVar: #message. m mapInstVar: #result ]. reader nextAs: BittrexResponse ]; get) inspect. ==>BittrexResponse success => true message => '' result => an Array(a Dictionary('BaseCurrency'->'BTC' 'BaseCurrencyLong'->'Bitcoin') ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum') 3. Finally parse into real objects the nested level holding the data you really want... Object subclass: #Market instanceVariableNames: 'MarketCurrency BaseCurrency MarketCurrencyLong BaseCurrencyLong MinTradeSize MarketName IsActive Created Notice IsSponsored LogoUrl' classVariableNames: '' package: 'Bittrex' (ZnClient new enforceHttpSuccess: true; accept: ZnMimeType applicationJson; contentReader: [ :entity | |reader| reader := (NeoJSONReader on: entity readStream). reader for: BittrexResponse do: [:m| m mapInstVar: #success. m mapInstVar: #message. (m mapInstVar: #result) valueSchema: #ArrayOfMarkets]. reader for: #ArrayOfMarkets customDo: [ :mapping | mapping listOfElementSchema: Market ]. reader mapInstVarsFor: Market. reader nextAs: BittrexResponse ]; get) inspect. ==>BittrexResponse success => true message => '' result => an Array(a Market(LTC) a Market(DOGE) a Market(VTC) a Market(PPC) a Market(FTC) a Market(RDD) ... Market(POWR) a Market(BTG) a Market(BTG) a Market(BTG) a Market(ADA) a Market(ENG) a Market(ENG)) WhooHoo! A couple of things remaining: * The instance variables of Market currently start with an upper-case to match the JSON fields. Lower-casing the first letter breaks things. What strategies can be used to conform here to Smalltalk conventions? Or is it easy enough to live with it? * In various posts I've seen mention of a class-side method #neoJsonMapping: but there is no explanation of this in the Enterprise Book. How might #neoJsonMapping: come into the picture for my use case above? cheers -ben |
Tx for your report :)
https://wordpress.com/post/pharoweekly.wordpress.com/2347 Did you look at SmartShackles? on our inria github repo. Because Santiago is extracting information from blockchains. On Sun, Dec 10, 2017 at 4:45 AM, Ben Coman <[hidden email]> wrote: > > On 5 December 2017 at 20:44, Sven Van Caekenberghe <[hidden email]> wrote: >> >> >> > On 5 Dec 2017, at 13:33, Ben Coman <[hidden email]> wrote: >> >> > @sven, I started reading Enterprise Pharo a couple of hours ago. >> > I don't quite get your section references. I presume you >> > don't mean "chapter 11 Persisting Objects with Voyage, 11.3 Enhancing >> > Storage" >> > That seems off topic. And "chapter 4 Zinc HTTP: The Client Side, 4.11 >> > Headers" >> > doesn't have a sub-part "3". >> >> I meant 11.3 in this page >> https://ci.inria.fr/pharo-contribution/job/EnterprisePharoBook/lastSuccessfulBuild/artifact/book-result/Zinc-HTTP-Server/Zinc-HTTP-Server.html >> a section called 11.3. A Zinc Client. >> >> > I guess part of what I'm interested in are patterns for hooking >> > NeoJSON up to parse a REST response into objects to build a wrapper >> > around a REST service. I see a chapter in Enterprise Pharo, which I'll >> > get to that soon. Perhaps I was premature asking before reading that, >> > but its good to have a few paths to explore. >> >> Here is a recent example >> http://forum.world.st/Another-example-of-invoking-a-REST-JSON-web-service-resolving-User-Agent-strings-tt5017489.html > > > > Thanks Sven. That helped a lot. I'd like to report success. > It may be useful to others to see how to progressively build up to parsing a > Nested JSON REST > > > 1. First parse the JSON into simple Dictionaries... > > (ZnClient new > url: 'https://bittrex.com/api/v1.1/public/getmarkets'; > enforceHttpSuccess: true; > accept: ZnMimeType applicationJson; > contentReader: [ :entity | NeoJSONReader fromString: entity contents ]; > get) inspect. > > ==>Dictionary( > 'success' ==> true > 'message' ==> '' > 'result ' ==> an Array(a Dictionary('BaseCurrency'->'BTC' > 'BaseCurrencyLong'->'Bitcoin') > ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum') > > > > 2. Then parse the first level response into a real object... > > Object subclass: #BittrexResponse > instanceVariableNames: 'success message result' > classVariableNames: '' > package: 'Bittrex' > > (ZnClient new > url: 'https://bittrex.com/api/v1.1/public/getmarkets'; > enforceHttpSuccess: true; > accept: ZnMimeType applicationJson; > contentReader: [ :entity | > (NeoJSONReader on: entity readStream) > mapInstVarsFor: BittrexResponse ; > nextAs: BittrexResponse ]; > get) inspect. > > ==>BittrexResponse > success => true > message => '' > result => an Array(a Dictionary('BaseCurrency'->'BTC' > 'BaseCurrencyLong'->'Bitcoin') > ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum') > > > Or alternatively... > (ZnClient new > url: 'https://bittrex.com/api/v1.1/public/getmarkets'; > enforceHttpSuccess: true; > accept: ZnMimeType applicationJson; > contentReader: [ :entity | |reader| > reader := (NeoJSONReader on: entity readStream). > reader for: BittrexResponse do: [:m| > m mapInstVar: #success. > m mapInstVar: #message. > m mapInstVar: #result ]. > reader nextAs: BittrexResponse ]; > get) inspect. > > ==>BittrexResponse > success => true > message => '' > result => an Array(a Dictionary('BaseCurrency'->'BTC' > 'BaseCurrencyLong'->'Bitcoin') > ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum') > > > 3. Finally parse into real objects the nested level holding the data you > really want... > > Object subclass: #Market > instanceVariableNames: 'MarketCurrency BaseCurrency MarketCurrencyLong > BaseCurrencyLong MinTradeSize MarketName IsActive Created Notice IsSponsored > LogoUrl' > classVariableNames: '' > package: 'Bittrex' > > (ZnClient new > url: 'https://bittrex.com/api/v1.1/public/getmarkets'; > enforceHttpSuccess: true; > accept: ZnMimeType applicationJson; > contentReader: [ :entity | |reader| > reader := (NeoJSONReader on: entity readStream). > reader for: BittrexResponse do: [:m| > m mapInstVar: #success. > m mapInstVar: #message. > (m mapInstVar: #result) valueSchema: #ArrayOfMarkets]. > reader for: #ArrayOfMarkets customDo: [ :mapping | mapping > listOfElementSchema: Market ]. > reader mapInstVarsFor: Market. > reader nextAs: BittrexResponse ]; > get) inspect. > > ==>BittrexResponse > success => true > message => '' > result => an Array(a Market(LTC) a Market(DOGE) a Market(VTC) a > Market(PPC) a Market(FTC) a Market(RDD) > ... Market(POWR) a Market(BTG) a Market(BTG) a Market(BTG) a Market(ADA) a > Market(ENG) a Market(ENG)) > > > WhooHoo! > > > A couple of things remaining: > > * The instance variables of Market currently start with an upper-case to > match the JSON fields. > Lower-casing the first letter breaks things. > What strategies can be used to conform here to Smalltalk conventions? > Or is it easy enough to live with it? > > * In various posts I've seen mention of a class-side method #neoJsonMapping: > but there is no explanation of this in the Enterprise Book. > How might #neoJsonMapping: come into the picture for my use case above? > > cheers -ben > |
On 10 December 2017 at 15:27, Stephane Ducasse <[hidden email]> wrote: Tx for your report :) I would hesitate to call where I'm up to right now as blockchain related. Its just the trading api of one exchange where the items traded happen to be bitcoins. But I have a growing interest in blockchains, so thanks for the tip... I didn't see anything relevant here... https://github.com/INRIA but with that product name I found... https://github.com/sbragagnolo/SmartShackle cheers -ben |
In reply to this post by Ben Coman
> On 10 Dec 2017, at 04:45, Ben Coman <[hidden email]> wrote: > > > On 5 December 2017 at 20:44, Sven Van Caekenberghe <[hidden email]> wrote: > > > On 5 Dec 2017, at 13:33, Ben Coman <[hidden email]> wrote: > > > @sven, I started reading Enterprise Pharo a couple of hours ago. > > I don't quite get your section references. I presume you > > don't mean "chapter 11 Persisting Objects with Voyage, 11.3 Enhancing Storage" > > That seems off topic. And "chapter 4 Zinc HTTP: The Client Side, 4.11 Headers" > > doesn't have a sub-part "3". > > I meant 11.3 in this page https://ci.inria.fr/pharo-contribution/job/EnterprisePharoBook/lastSuccessfulBuild/artifact/book-result/Zinc-HTTP-Server/Zinc-HTTP-Server.html a section called 11.3. A Zinc Client. > > > I guess part of what I'm interested in are patterns for hooking > > NeoJSON up to parse a REST response into objects to build a wrapper > > around a REST service. I see a chapter in Enterprise Pharo, which I'll > > get to that soon. Perhaps I was premature asking before reading that, > > but its good to have a few paths to explore. > > Here is a recent example http://forum.world.st/Another-example-of-invoking-a-REST-JSON-web-service-resolving-User-Agent-strings-tt5017489.html > > > Thanks Sven. That helped a lot. I'd like to report success. > It may be useful to others to see how to progressively build up to parsing a Nested JSON REST > > > 1. First parse the JSON into simple Dictionaries... > > (ZnClient new > url: 'https://bittrex.com/api/v1.1/public/getmarkets'; > enforceHttpSuccess: true; > accept: ZnMimeType applicationJson; > contentReader: [ :entity | NeoJSONReader fromString: entity contents ]; > get) inspect. > > ==>Dictionary( > 'success' ==> true > 'message' ==> '' > 'result ' ==> an Array(a Dictionary('BaseCurrency'->'BTC' 'BaseCurrencyLong'->'Bitcoin') > ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum') > > > > 2. Then parse the first level response into a real object... > > Object subclass: #BittrexResponse > instanceVariableNames: 'success message result' > classVariableNames: '' > package: 'Bittrex' > > (ZnClient new > url: 'https://bittrex.com/api/v1.1/public/getmarkets'; > enforceHttpSuccess: true; > accept: ZnMimeType applicationJson; > contentReader: [ :entity | > (NeoJSONReader on: entity readStream) > mapInstVarsFor: BittrexResponse ; > nextAs: BittrexResponse ]; > get) inspect. > > ==>BittrexResponse > success => true > message => '' > result => an Array(a Dictionary('BaseCurrency'->'BTC' 'BaseCurrencyLong'->'Bitcoin') > ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum') > > > Or alternatively... > (ZnClient new > url: 'https://bittrex.com/api/v1.1/public/getmarkets'; > enforceHttpSuccess: true; > accept: ZnMimeType applicationJson; > contentReader: [ :entity | |reader| > reader := (NeoJSONReader on: entity readStream). > reader for: BittrexResponse do: [:m| > m mapInstVar: #success. > m mapInstVar: #message. > m mapInstVar: #result ]. > reader nextAs: BittrexResponse ]; > get) inspect. > > ==>BittrexResponse > success => true > message => '' > result => an Array(a Dictionary('BaseCurrency'->'BTC' 'BaseCurrencyLong'->'Bitcoin') > ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum') > > > 3. Finally parse into real objects the nested level holding the data you really want... > > Object subclass: #Market > instanceVariableNames: 'MarketCurrency BaseCurrency MarketCurrencyLong BaseCurrencyLong MinTradeSize MarketName IsActive Created Notice IsSponsored LogoUrl' > classVariableNames: '' > package: 'Bittrex' > > (ZnClient new > url: 'https://bittrex.com/api/v1.1/public/getmarkets'; > enforceHttpSuccess: true; > accept: ZnMimeType applicationJson; > contentReader: [ :entity | |reader| > reader := (NeoJSONReader on: entity readStream). > reader for: BittrexResponse do: [:m| > m mapInstVar: #success. > m mapInstVar: #message. > (m mapInstVar: #result) valueSchema: #ArrayOfMarkets]. > reader for: #ArrayOfMarkets customDo: [ :mapping | mapping listOfElementSchema: Market ]. > reader mapInstVarsFor: Market. > reader nextAs: BittrexResponse ]; > get) inspect. > > ==>BittrexResponse > success => true > message => '' > result => an Array(a Market(LTC) a Market(DOGE) a Market(VTC) a Market(PPC) a Market(FTC) a Market(RDD) > ... Market(POWR) a Market(BTG) a Market(BTG) a Market(BTG) a Market(ADA) a Market(ENG) a Market(ENG)) > > > WhooHoo! Great, I'm glad you're happy. > A couple of things remaining: > > * The instance variables of Market currently start with an upper-case to match the JSON fields. > Lower-casing the first letter breaks things. > What strategies can be used to conform here to Smalltalk conventions? > Or is it easy enough to live with it? You can map properties (the key/values in JSON maps) using different mechanisms. Directly via instance variables, via accessors and via blocks. In the first two cases, the first approach is indeed to match the names literally. But there are also the variants #mapInstVar:to: and #mapAccessor:to: to have a different name. mapping mapInstVar: #markerCurrency to: #MarketCurrency > * In various posts I've seen mention of a class-side method #neoJsonMapping: > but there is no explanation of this in the Enterprise Book. > How might #neoJsonMapping: come into the picture for my use case above? A mapper (reader or writer) looks for mappings in itself (like how you used it) but also on the class side of schema/class names. So instead of doing the mapping in the construction of the reader/writer you can write a fixed on the class side. Look for implementors of #neoJsonMapping: The difference is mainly that scope: per reader/writer allows for different mappings depending on use case, while the class side #neoJsonMapping: is global. Some people like a schema definition in one place, other like a distributed one (with inheritance options). Sven > cheers -ben > |
In reply to this post by Ben Coman
Hi Sven (et al),
On 10 December 2017 at 11:45, Ben Coman <[hidden email]> wrote:
So the code of [3.] above works fine when the raw response looks like this... ==> {"success":true, "message":"", "result": [{"MarketName":"BTC-LTC},{"MarketName":"BTC-NXT}] } But of course [3.] doesn't work when the raw response looks like this... (ZnClient new get) inspect. ==> {"success":true, "message":"", "result":{"Bid":0.01765465,"Ask":0.01769000,"Last":0.01763350} } since result is a json-object not a json-array. But I can't work out how to map the JSON object into the 'result' instance variable of BittrexResponse, where... BittrexObject subclass: #BittrexTicker instanceVariableNames: 'Bid Ask Last' classVariableNames: '' package: 'Bittrex' My best guesses so far are uncommenting either [A.] or [B.] below... (ZnClient new enforceHttpSuccess: true; accept: ZnMimeType applicationJson; contentReader: [ :entity | |reader| reader := (NeoJSONReader on: entity readStream). reader for: BittrexResponse do: [:m| m mapInstVar: #success. m mapInstVar: #message. (m mapInstVar: #result) valueSchema: #BittrexTicker]. "A. reader for: BittrexTicker do: [ :m| m mapInstVar: #Bid]." "B. reader for: #BittrexTicker customDo: [ :mapping | mapping mapWithValueSchema: BittrexTicker]." reader nextAs: BittrexResponse ]; get). Can you advise what formulation is required? cheers -ben |
Yeah, that is the main problem with JSON. It cannot capture that variability in its own spec. Sure, there are tons of extensions on top of JSON to address these aspects, but NeoJSON only deals with the raw spec. And any server can use its own version.
Simple answer is indeed: getmarkets and getticker return different schema, since you know what you call, you can map accordingly. (And hope the server does not do further variability). There cannot be one global mapping. > On 13 Dec 2017, at 05:37, Ben Coman <[hidden email]> wrote: > > Hi Sven (et al), > > On 10 December 2017 at 11:45, Ben Coman <[hidden email]> wrote: > > 3. Finally parse into real objects the nested level holding the data you really want... > > Object subclass: #Market > instanceVariableNames: 'MarketCurrency BaseCurrency MarketCurrencyLong BaseCurrencyLong MinTradeSize MarketName IsActive Created Notice IsSponsored LogoUrl' > classVariableNames: '' > package: 'Bittrex' > > (ZnClient new > url: 'https://bittrex.com/api/v1.1/public/getmarkets'; > enforceHttpSuccess: true; > accept: ZnMimeType applicationJson; > contentReader: [ :entity | |reader| > reader := (NeoJSONReader on: entity readStream). > reader for: BittrexResponse do: [:m| > m mapInstVar: #success. > m mapInstVar: #message. > (m mapInstVar: #result) valueSchema: #ArrayOfMarkets]. > reader for: #ArrayOfMarkets customDo: [ :mapping | mapping listOfElementSchema: Market ]. > reader mapInstVarsFor: Market. > reader nextAs: BittrexResponse ]; > get) inspect. > > ==>BittrexResponse > success => true > message => '' > result => an Array(a Market(LTC) a Market(DOGE) a Market(VTC) a Market(PPC) a Market(FTC) a Market(RDD) > ... Market(POWR) a Market(BTG) a Market(BTG) a Market(BTG) a Market(ADA) a Market(ENG) a Market(ENG)) > > So the code of [3.] above works fine when the raw response looks like this... > (ZnClient new > url: 'https://bittrex.com/api/v1.1/public/getmarkets'; > get) inspect. > ==> {"success":true, > "message":"", > "result": [{"MarketName":"BTC-LTC},{"MarketName":"BTC-NXT}] > } > > But of course [3.] doesn't work when the raw response looks like this... > (ZnClient new > url: 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LTC'; > get) inspect. > ==> {"success":true, > "message":"", > "result":{"Bid":0.01765465,"Ask":0.01769000,"Last":0.01763350} > } > > since result is a json-object not a json-array. But I can't work out > how to map the JSON object into the 'result' instance variable of BittrexResponse, > where... > > BittrexObject subclass: #BittrexTicker > instanceVariableNames: 'Bid Ask Last' > classVariableNames: '' > package: 'Bittrex' > > > My best guesses so far are uncommenting either [A.] or [B.] below... > > (ZnClient new > url: 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LTC'; > enforceHttpSuccess: true; > accept: ZnMimeType applicationJson; > contentReader: [ :entity | |reader| > reader := (NeoJSONReader on: entity readStream). > reader for: BittrexResponse do: [:m| > m mapInstVar: #success. > m mapInstVar: #message. > (m mapInstVar: #result) valueSchema: #BittrexTicker]. > "A. reader for: BittrexTicker do: [ :m| > m mapInstVar: #Bid]." > "B. reader for: #BittrexTicker customDo: [ :mapping | > mapping mapWithValueSchema: BittrexTicker]." > reader nextAs: BittrexResponse ]; > get). > > > Can you advise what formulation is required? > cheers -ben |
On 13 December 2017 at 14:40, Sven Van Caekenberghe <[hidden email]> wrote: Yeah, that is the main problem with JSON. It cannot capture that variability in its own spec. Sure, there are tons of extensions on top of JSON to address these aspects, but NeoJSON only deals with the raw spec. And any server can use its own version. Thats cool. I'm not looking for one global mapping to simultaneously handle getmarkets and getticker together. I'm just looking to handle getticker in isolation. I can't work that part out. cheers -ben
|
> On 13 Dec 2017, at 07:49, Ben Coman <[hidden email]> wrote: > > > > On 13 December 2017 at 14:40, Sven Van Caekenberghe <[hidden email]> wrote: > Yeah, that is the main problem with JSON. It cannot capture that variability in its own spec. Sure, there are tons of extensions on top of JSON to address these aspects, but NeoJSON only deals with the raw spec. And any server can use its own version. > > Simple answer is indeed: getmarkets and getticker return different schema, since you know what you call, you can map accordingly. (And hope the server does not do further variability). There cannot be one global mapping. > > > Thats cool. I'm not looking for one global mapping to simultaneously handle getmarkets and getticker together. > I'm just looking to handle getticker in isolation. I can't work that part out. Hmm, your result ivar in response is of type ticker which maps its 3 ivars. So uncomment A but add all 3 ivars to ticker. no ? > cheers -ben > > > > On 13 Dec 2017, at 05:37, Ben Coman <[hidden email]> wrote: > > > > Hi Sven (et al), > > > > On 10 December 2017 at 11:45, Ben Coman <[hidden email]> wrote: > > > > 3. Finally parse into real objects the nested level holding the data you really want... > > > > Object subclass: #Market > > instanceVariableNames: 'MarketCurrency BaseCurrency MarketCurrencyLong BaseCurrencyLong MinTradeSize MarketName IsActive Created Notice IsSponsored LogoUrl' > > classVariableNames: '' > > package: 'Bittrex' > > > > (ZnClient new > > url: 'https://bittrex.com/api/v1.1/public/getmarkets'; > > enforceHttpSuccess: true; > > accept: ZnMimeType applicationJson; > > contentReader: [ :entity | |reader| > > reader := (NeoJSONReader on: entity readStream). > > reader for: BittrexResponse do: [:m| > > m mapInstVar: #success. > > m mapInstVar: #message. > > (m mapInstVar: #result) valueSchema: #ArrayOfMarkets]. > > reader for: #ArrayOfMarkets customDo: [ :mapping | mapping listOfElementSchema: Market ]. > > reader mapInstVarsFor: Market. > > reader nextAs: BittrexResponse ]; > > get) inspect. > > > > ==>BittrexResponse > > success => true > > message => '' > > result => an Array(a Market(LTC) a Market(DOGE) a Market(VTC) a Market(PPC) a Market(FTC) a Market(RDD) > > ... Market(POWR) a Market(BTG) a Market(BTG) a Market(BTG) a Market(ADA) a Market(ENG) a Market(ENG)) > > > > So the code of [3.] above works fine when the raw response looks like this... > > (ZnClient new > > url: 'https://bittrex.com/api/v1.1/public/getmarkets'; > > get) inspect. > > ==> {"success":true, > > "message":"", > > "result": [{"MarketName":"BTC-LTC},{"MarketName":"BTC-NXT}] > > } > > > > But of course [3.] doesn't work when the raw response looks like this... > > (ZnClient new > > url: 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LTC'; > > get) inspect. > > ==> {"success":true, > > "message":"", > > "result":{"Bid":0.01765465,"Ask":0.01769000,"Last":0.01763350} > > } > > > > since result is a json-object not a json-array. But I can't work out > > how to map the JSON object into the 'result' instance variable of BittrexResponse, > > where... > > > > BittrexObject subclass: #BittrexTicker > > instanceVariableNames: 'Bid Ask Last' > > classVariableNames: '' > > package: 'Bittrex' > > > > > > My best guesses so far are uncommenting either [A.] or [B.] below... > > > > (ZnClient new > > url: 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LTC'; > > enforceHttpSuccess: true; > > accept: ZnMimeType applicationJson; > > contentReader: [ :entity | |reader| > > reader := (NeoJSONReader on: entity readStream). > > reader for: BittrexResponse do: [:m| > > m mapInstVar: #success. > > m mapInstVar: #message. > > (m mapInstVar: #result) valueSchema: #BittrexTicker]. > > "A. reader for: BittrexTicker do: [ :m| > > m mapInstVar: #Bid]." > > "B. reader for: #BittrexTicker customDo: [ :mapping | > > mapping mapWithValueSchema: BittrexTicker]." > > reader nextAs: BittrexResponse ]; > > get). > > > > > > Can you advise what formulation is required? > > cheers -ben > > > |
Thats what I gathered from the test examples (and [B.] was really grasping at straws.) I just assumed my understanding was lacking. Attacking it with renewed confidence from your response, tracing through I found the problem was the line before [A.] should have been.... (m mapInstVar: #result) valueSchema: BittrexTicker]. not (m mapInstVar: #result) valueSchema: #BittrexTicker]. now it works. thanks for your assistance. cheers -ben |
In reply to this post by Ben Coman
https://github.com/RMODINRIA-Blockchain
Inria = 200 teams 4000 researchers :) On Sun, Dec 10, 2017 at 9:12 AM, Ben Coman <[hidden email]> wrote: > > > On 10 December 2017 at 15:27, Stephane Ducasse <[hidden email]> > wrote: >> >> Tx for your report :) >> >> https://wordpress.com/post/pharoweekly.wordpress.com/2347 >> >> Did you look at SmartShackles? on our inria github repo. Because >> Santiago is extracting information from blockchains. >> > > I would hesitate to call where I'm up to right now as blockchain related. > Its just the trading api of one exchange where the items traded happen to be > bitcoins. > But I have a growing interest in blockchains, so thanks for the tip... > > I didn't see anything relevant here... https://github.com/INRIA > but with that product name I found... > https://github.com/sbragagnolo/SmartShackle > > cheers -ben |
Free forum by Nabble | Edit this page |