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
>> 
> 
> 


Reply via email to