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
> 


Reply via email to