> On 25 Sep 2017, at 17:14, Juraj Kubelka <juraj.kube...@icloud.com> wrote:
> 
> 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.

I did, thanks !

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

STON has no options for more than one mapping (the 'system' one).

Schema evolution is always a hard problem, using temporary structures seems the 
simplest solution.

The STON specification has not changed significantly since I first wrote it 
years ago. The implementation and the number of cases handled has though.

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

Yes, other mappings should be respected.

Sven

> 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