Hi Sven, thank you!
In the NeoJSON repository, you likely want to merge Neo-JSON-Core-SvenVanCaekenberghe.43 and 44. And the same for the test cases. > On Sep 24, 2017, at 06:05, Sven Van Caekenberghe <s...@stfx.eu> wrote: > > Hi Juraj, > > This would be a simpler form of the type/class tags that are often used in > JSON encoding. Since there are many ways to do this, there cannot be one > solution. NeoJSON mapping was not designed to cover these cases. Nor is JSON > meant to do this. STON is one (rather elegant if I may say so) answer to this > problem. Your example would be encoded as > > [ > PngAttachement { #url:'http://example.com/random-name.txt', > #fileName:'chapter-one.txt' }, > TxtAttachement { #url:'http://example.com/random-name.png', > #fileName:'image.png' } > ] About STON usage, I wonder how it works in terms of a data structure evolution. Currently I use JSON format to store a data permanently. The data are supposed to be accesible for a long time (years). I am pretty sure that the protocol will evolve and we will end up with several JSON versions. I think that having different JSON mapping schemes will allow to map old JSON versions to a latest object structure (or to several different versions if necessary). For the example above, think of the scenario that PngAttachment does not exist anymore. In a simple scenario there could be an ImageAttachment class instead with the same instance variables. In a more complex scenario, there could be a AttachmentTwo { #type: AttachmentTwoType { … instances … }, #path: AttachmentTwoPath { … instances … } }. I can use JSON mappings that transform the old JSON version to the new class structure. I am not sure if STON is that flexible. What do you think? Do I miss something? > > Still, the question of how to do this kind of dynamic decoding is an > interesting challenge. Please update to the following commits: > > === > Name: Neo-JSON-Core-SvenVanCaekenberghe.44 > Author: SvenVanCaekenberghe > Time: 22 September 2017, 3:24:51.679449 pm > UUID: f1ebeade-2816-0d00-a04c-ae370e598362 > Ancestors: Neo-JSON-Core-SvenVanCaekenberghe.42 > > Implement some missing code in NeoJSONCustomMapping (the writing part of > #listOfElementSchema: #listOfType:andElementSchema: #mapWithValueSchema:) > > Add NeoJSONStreamingWriter>>#writeElementAs: > > Add NeoJSONMappingTests>>#testDynamicTyping as an example > === > Name: Neo-JSON-Tests-SvenVanCaekenberghe.41 > Author: SvenVanCaekenberghe > Time: 22 September 2017, 3:25:09.881494 pm > UUID: a9a900e0-2816-0d00-a04d-a6fb0e598362 > Ancestors: Neo-JSON-Tests-SvenVanCaekenberghe.39 > > Implement some missing code in NeoJSONCustomMapping (the writing part of > #listOfElementSchema: #listOfType:andElementSchema: #mapWithValueSchema:) > > Add NeoJSONStreamingWriter>>#writeElementAs: > > Add NeoJSONMappingTests>>#testDynamicTyping as an example > === > > Now you can do as follows: > > NeoJSONMappingTests>>#testDynamicTyping > | data customMapping json result | > data := Array with: #foo->1 with: #(foo 2). > "The idea is to map a key value combination as either a classic association > or a simple pair, > using key & value properties as well as a type property to distinguish > between the two" > customMapping := [ :mapper | > mapper > for: #AssocOrPair customDo: [ :mapping | > mapping > encoder: [ :x | > x isArray > ifTrue: [ { #type->#pair. #key->x first. #value->x second } > asDictionary ] > ifFalse: [ { #type->#assoc. #key->x key. #value->x value } > asDictionary ] ]; > decoder: [ :x | > (x at: #type) = #pair > ifTrue: [ Array with: (x at: #key) with: (x at: #value) ] > ifFalse: [ (x at: #key) -> (x at: #value)] ] ]; > for: #ArrayOfAssocOrPair customDo: [ :mapping | > mapping listOfType: Array andElementSchema: #AssocOrPair ]; > yourself ]. > json := String streamContents: [ :out | > (customMapping value: (NeoJSONWriter on: out)) nextPut: data as: > #ArrayOfAssocOrPair ]. > result := (customMapping value: (NeoJSONReader on: json readStream)) nextAs: > #ArrayOfAssocOrPair. > self assert: result equals: data > > Everything is virtual (not implemented as methods on actual classes) which > makes the example self contained and non intrusive, but not very elegant, nor > object oriented or extendable. Also, it is not as efficient as NeoJSON > mapping was designed for, since it creates intermediate structures. Nice example. If #decoder: and #encoder: are used, is it still possible to use inner mappings? For example in my previous example, one instance variable is a ZnUrl instance that is stored as a String. Can I say “use ZnUrl mapping in for this intermediate structure”? Well, I understand that I can hard code it directly in the the #decoder: and #encoder: messages using #asZnUrl and #asString messages. I am thinking about a more complicated structures that involves more mapping definitions. Thanks! Juraj > > HTH, > > Sven > >> On 21 Sep 2017, at 22:07, Juraj Kubelka <juraj.kube...@icloud.com> wrote: >> >> Hi, >> >> By studying the NeoJSON book chapter (Pharo Enterprise), I do not understand >> how to modify the following example: >> >> -=-=-=-=-=-=-=- >> "Let us say that we have an Attachment class..." >> >> Object subclass: #Attachment >> instanceVariableNames: 'url fileName' >> classVariableNames: '' >> package: 'NeoJSON-Use-Case'. >> >> "...with url: and fileName: methods." >> Attachment compile: 'url: anObject', String cr, String tab, 'url := >> anObject' classified: 'accessing'. >> Attachment compile: 'fileName: anObject', String cr, String tab, 'fileName >> := anObject' classified: 'accessing'. >> >> "Let's create a collection of two instances:" >> collectionOne := { >> Attachment new >> url: 'http://example.com/random-name.txt' asZnUrl; >> fileName: 'chapter-one.txt' >> yourself. >> Attachment new >> url: 'http://example.com/random-name.png' asZnUrl; >> fileName: 'image.png'; >> yourself. >> }. >> >> "And let's map it to a JSON structure:" >> String streamContents: [ :aStream | >> (NeoJSONWriter on: aStream) >> for: #CollectionOfAttachments customDo: [ :mapping | >> mapping listOfElementSchema: Attachment ]; >> mapAllInstVarsFor: Attachment; >> for: ZnUrl customDo: [ :mapping | >> mapping encoder: [ :aZnUrl | >> aZnUrl asString ] ]; >> nextPut: collectionOne as: #CollectionOfAttachments. >> ]. >> >> "And read the JSON structure:" >> (NeoJSONReader on: >> '[{"url":"http://example.com/random-name.txt","fileName":"chapter-one.txt"},{"url":"http://example.com/random-name.png","fileName":"image.png"}]' >> readStream) >> for: #CollectionOfAttachments customDo: [ :mapping | >> mapping listOfElementSchema: Attachment ]; >> for: Attachment do: [ :mapping | >> mapping mapInstVar: 'fileName'. >> (mapping mapInstVar: 'url') valueSchema: ZnUrl ]; >> for: ZnUrl customDo: [ :mapping | >> mapping decoder: [ :string | >> string asZnUrl ] ]; >> nextAs: #CollectionOfAttachments. >> >> "=======================" >> “The previous example works perfectly, including the ZnUrl mapping (notice >> that url variables have ZnUrl instances). >> >> Now, let's say that we want to distinguish PNG and TXT attachments. >> For that reason we will create two Attachment subclasses..." >> >> Attachment subclass: #PngAttachment >> instanceVariableNames: '' >> classVariableNames: '' >> package: 'NeoJSON-Use-Case'. >> >> Attachment subclass: #TxtAttachment >> instanceVariableNames: '' >> classVariableNames: '' >> package: 'NeoJSON-Use-Case'. >> >> "...with type methods that might be used in a new JSON structure:" >> PngAttachment compile: 'type', String cr, String tab, '^ ''png''' >> classified: 'accessing'. >> TxtAttachment compile: 'type', String cr, String tab, '^ ''txt''' >> classified: 'accessing'. >> >> "Let's create a collection with the PNG and TXT instances:" >> collectionTwo := { >> TxtAttachment new >> url: 'http://example.com/random-name.txt' asZnUrl; >> fileName: 'chapter-one.txt' >> yourself. >> PngAttachment new >> url: 'http://example.com/random-name.png' asZnUrl; >> fileName: 'image.png'; >> yourself. >> }. >> >> "How can I modify NeoJSONWriter and NeoJSONReader mappings? >> >> The JSON structure should (ideally) looks like this:" >> >> '[ >> >> {“type”:”txt”,"url":"http://example.com/random-name.txt","fileName":"chapter-one.txt”}, >> >> {“type”:”png”,"url":"http://example.com/random-name.png","fileName":"image.png”} >> ]' >> -=-=-=-=-=-=-=- >> >> I would like to keep the mapping isolated (without defining neoJsonOn: >> methods). >> >> Thank you! >> Juraj >> > >