> On 10 Dec 2017, at 04:45, Ben Coman <b...@openinworld.com> wrote: > > > On 5 December 2017 at 20:44, Sven Van Caekenberghe <s...@stfx.eu> wrote: > > > On 5 Dec 2017, at 13:33, Ben Coman <b...@openinworld.com> 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 >