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' } ] 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. 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 >