hi Sven,
With NeoJSON I have found several times in handling lists that I need to indirect through #for:customDo: like this...... reader for: ExercismProblems do: [ :mapping | self halt. (mapper mapInstVar: #problems) valueSchema: #ArrayOfProblems ]. reader for: #ArrayOfProblems customDo: [ :mapping | mapper listOfElementSchema: ExercismProblem ]. when I'd really like to be simpler and more concise like this... reader for: ExercismProblems do: [ :mapping | (mapping mapInstVar: #problems) listOfType: Array withElementSchema: ExercismProblem ].
so I'm seeking your feedback on the following hack which facilitates that. (and if I missed something that already makes it simpler)
----------------------------
Object subclass: #NeoJSONPropertyMapping instanceVariableNames: 'propertyName valueSchema getter setter collectionClass' "Added collectionClass" NeoJSONPropertyMapping >> listOfType: aCollectionClass
withElementSchema: elementSchema collectionClass := aCollectionClass. valueSchema := elementSchema NeoJSONPropertyMapping >> readObject: anObject from: jsonReader | value | value := collectionClass ifNil: [jsonReader nextAs: valueSchema] "nil by default retains the original behaviour" ifNotNil: [jsonReader nextList: collectionClass of: valueSchema ]. setter value: anObject value: value NeoJSONReader >> nextList: nextListClass of: schema "copied from #nextAs" "Secondary interface to parse JSON. Return a list of objects, each element according to schema." ^ nextListClass streamContents: [ :stream | self parseListDo: [ stream nextPut: (self nextAs: schema) ] ] ---------------------------- Sample data is shown at: https://github.com/bencoman/pharogui-exercism/blob/master/README.md with the following code to access it. Object subclass: #ExercismAPI instanceVariableNames: 'client rawResponse jsonResponse success message result' classVariableNames: 'ApiKey' package: 'Exercism' ExercismAPI class >> configureApiKey: key ApiKey := key.
self path: 'v2/exercises/' , trackIdString.
ExercismAPI >> fetchTrack: trackIdString |znclient response| ApiKey ifNil: [ self error: 'ApiKey not configured. See class-side ExcercismAPI' ]. znclient := ZnClient new https; enforceHttpSuccess: true; accept: ZnMimeType applicationJson; host: 'x.exercism.io' . znclient path: 'v2/exercises/' , trackIdString. znclient queryAt: 'key' put: ApiKey. ^ [ response := znclient get ] on: ZnHttpUnsuccessful do: [ :exception | Transcript crShow: exception ] fetchJsonTrack: trackIdString | reader | reader := NeoJSONReader on: (self fetchTrack: trackIdString) readStream. reader for: ExercismProblems do: [ :mapper | (mapper mapInstVar: #problems) listOfType: Array andElementSchema: ExercismProblem ]. ^ reader nextAs: ExercismProblems In Playground... ExercismAPI configureApiKey: 'ec60c50cb50d4ffaa97903d5ebb3d5ff'. (ExercismAPI new fetchJsonTrack: 'go') inspect I'll reset the apikey shortly. You can get a new one from http://exercism.io/account/key. cheers -ben |
Hmm, Ben,
Did you see NeoJSONCustomMapping's mapping method category ? There you can already find #listOfElementSchema: #listOfType: #listOfType:andElementSchema: as well as #mapWithValueSchema: I am guessing they do what you want, no ? Or am I missing something as well ? Sven > On 16 Jun 2018, at 20:27, Ben Coman <[hidden email]> wrote: > > hi Sven, > > With NeoJSON I have found several times in handling lists that > I need to indirect through #for:customDo: like this...... > > reader > for: ExercismProblems > do: [ :mapping | > self halt. > (mapper mapInstVar: #problems) valueSchema: #ArrayOfProblems ]. > reader > for: #ArrayOfProblems > customDo: [ :mapping | > mapper listOfElementSchema: ExercismProblem ]. > > > when I'd really like to be simpler and more concise like this... > > reader > for: ExercismProblems > do: [ :mapping | > (mapping mapInstVar: #problems) listOfType: Array withElementSchema: ExercismProblem ]. > > so I'm seeking your feedback on the following hack which facilitates that. > (and if I missed something that already makes it simpler) > > ---------------------------- > Object subclass: #NeoJSONPropertyMapping > instanceVariableNames: 'propertyName valueSchema getter setter collectionClass' "Added collectionClass" > > > NeoJSONPropertyMapping >> listOfType: aCollectionClass withElementSchema: elementSchema > collectionClass := aCollectionClass. > valueSchema := elementSchema > > > NeoJSONPropertyMapping >> readObject: anObject from: jsonReader > | value | > value := collectionClass > ifNil: [jsonReader nextAs: valueSchema] "nil by default retains the original behaviour" > ifNotNil: [jsonReader nextList: collectionClass of: valueSchema ]. > setter value: anObject value: value > > > NeoJSONReader >> nextList: nextListClass of: schema "copied from #nextAs" > "Secondary interface to parse JSON. > Return a list of objects, each element according to schema." > > ^ nextListClass streamContents: [ :stream | > self parseListDo: [ > stream nextPut: (self nextAs: schema) ] ] > ---------------------------- > > Sample data is shown at: https://github.com/bencoman/pharogui-exercism/blob/master/README.md > with the following code to access it. > > Object subclass: #ExercismAPI > instanceVariableNames: 'client rawResponse jsonResponse success message result' > classVariableNames: 'ApiKey' > package: 'Exercism' > > > ExercismAPI class >> configureApiKey: key > ApiKey := key. > > > ExercismAPI >> track: trackIdString > self path: 'v2/exercises/' , trackIdString. > > > ExercismAPI >> fetchTrack: trackIdString > |znclient response| > ApiKey ifNil: [ self error: 'ApiKey not configured. See class-side ExcercismAPI' ]. > znclient := ZnClient new > https; > enforceHttpSuccess: true; > accept: ZnMimeType applicationJson; > host: 'x.exercism.io' . > znclient path: 'v2/exercises/' , trackIdString. > znclient queryAt: 'key' put: ApiKey. > ^ [ response := znclient get ] > on: ZnHttpUnsuccessful > do: [ :exception | Transcript crShow: exception ] > > > fetchJsonTrack: trackIdString > | reader | > reader := NeoJSONReader on: (self fetchTrack: trackIdString) readStream. > reader > for: ExercismProblems > do: [ :mapper | > (mapper mapInstVar: #problems) listOfType: Array andElementSchema: ExercismProblem ]. > ^ reader nextAs: ExercismProblems > > > In Playground... > > ExercismAPI configureApiKey: 'ec60c50cb50d4ffaa97903d5ebb3d5ff'. > > (ExercismAPI new fetchJsonTrack: 'go') inspect > > > I'll reset the apikey shortly. You can get a new one from http://exercism.io/account/key. > > cheers -ben |
On 17 June 2018 at 02:47, Sven Van Caekenberghe <[hidden email]> wrote: Hmm, Ben,
#listOfType:andElementSchema: as well as #mapWithValueSchema: Yes. Indeed thats where I copied my selector naming from, although I switched "with" for ''and" NeoJSONPropertyMapping >> listOfType:withElementSchema: while instance variable interaction seems only possible using "reader for: ... do:" So it seems NeoJSONCustomMapping lacks #mapAccessor* methods while NeoJSONObjectMapping lacks #listOfType:* methods and "never the twain shall meet". I didn't look closely at #mapWithValueSchema:
but its covering tests seem to operate on Dictionaries rather than Arrays. I am guessing they do what you want, no ? Or am I missing something as well ? Yes, but how to mix those list* methods with poking the result into an instance variable to be able to "assign a list of elements to an instance variable" as a one liner? I'd be glad to be shown another way to do this for the given data. I see a few #testLists examples, but none that assign the result to an instance variable. NeoJSONExamplesTests has some "lists of objects" tests, but no "objects with lists" A useful example would be #testPolygonPoints... NeoJSONExamplesTests >> testPolygonPoints | polygon polygonJson result | polygon := Polygon new vertices: { 1@2. 3@4. 5@6 }. polygonJson := '{ "vertices": [
"I'm not clear on how to do the writer side of things"
{ "x" : 1, "y" : 2 }, { "x" : 3, "y" : 4 }, { "x" : 5, "y" : 6 }] }'. result := (NeoJSONReader on: polygonJson readStream) mapInstVarsFor: Point; for: Polygon do: [ :mapping | (mapping mapInstVar: #vertices) listOfType: Array withElementSchema: Point]; nextAs: Polygon. self assert: result equals: polygon Some equality support is needed for Polygon (PathShape) (my best guess, discussion of this might be worth a separate thread) PathShape >> = aPathShape |otherVertices equal| otherVertices := aPathShape vertices. (self species = aPathShape species) & (vertices size = otherVertices size) ifFalse: [ ^false ]. 1 to: vertices size do: [ :i | (vertices at: i) = (otherVertices at: i) ifFalse: [^false] ]. ^true PathShape >> hash |hash| hash := 0. 1 to: vertices size do: [ :i | hash bitXor: (vertices at: i) ]. ^hash cheers -ben
cheers -ben |
Free forum by Nabble | Edit this page |