#1 2015-02-15 05:33:46

RIL
Member
Registered: 2015-02-15
Posts: 4

Sorting content in Variants & TDocVariant ?

I'm using JSON format for exporting / importing UML model info (from EA) and for file-diff purposes I'd like to be able to sort JSON pairs and arrays (I can then test-compare Export.txt with Import.txt, for example).

But I'm not experienced with the SynCommon.pas unit and have a problem finding out how to sort the two examples below :

    v, v2: Variant;
begin
    TDocVariant.New(v);
    TDocVariant.New(v2);

    v := _Json( '{ "B":"Bee","C":"Sii","A":"Ei" }' );
    // How do I sort v in order to get 
    //          '{ "A":"Ei","B":"Bee","C":"Sii" }' ?

    v2 := _Json( '{ "B":"Bee"},{"C":"Sii"},{"A":"Ei"}' );
    // How do I sort v2 in order to get :
    //          '{"A":"Ei"},{"B":"Bee"},{"C":"Sii"}' ?

I have tried dirty hacks like copying the content to TStrings, but I keep loosing data (and with big models such hacks will not perform).

What would be the fastest and simplest way to sort by name, ascending?

// Rolf Lampa

Last edited by RIL (2015-02-15 05:34:54)

Offline

#2 2015-02-15 07:01:55

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,609
Website

Re: Sorting content in Variants & TDocVariant ?

I've added the TDocVariantData.SortByName method.
See http://synopse.info/fossil/info/b38f4de17d
It will work for your first kind of content.

Your v2 is not a valid JSON content.
I suppose there are some [   ]  missing to make it a JSON array.
But there is no simple way of sorting a JSON array.
If there is only one property per nested object, why not... but it is not a common case.
But if there are several properties per nested object, how do we sort the array?

Offline

#3 2015-02-15 08:23:36

RIL
Member
Registered: 2015-02-15
Posts: 4

Re: Sorting content in Variants & TDocVariant ?

ab wrote:

I've added the TDocVariantData.SortByName method.
See http://synopse.info/fossil/info/b38f4de17d

Your v2 is not a valid JSON content.  I suppose there are some [   ]  missing to make it a JSON array.

Thank you very much for the SortByName!

And yes, the brackets were missing:

v2 := _Json( '[{ "B":"Bee"},{"C":"Sii"},{"A":"Ei"}]' );

I actually collect my lists (via RTTI) one by one, returning them as string, and then add them as JSON arrays to the owning object, like so:

    vMain, vTV: Variant;
begin
    // Retrieving RTTI Context etc.
    ...
    // Add an empty Array
    vMain.Add('TaggedValues', _Arr([]));

    props := c.GetIndexedProperty('TaggedValues');
    Cnt := c.GetProperty('TaggedValuesCount').GetValue(aInstance).AsInteger;
    for i := 0 to Cnt - 1 do
    begin
        Obj := props.GetValue(aInstance, [i]).AsObject;
        vTV := _Json( GetTaggedValuesAsJsonStr(Obj) ); // '[{"name":"val"},{...}]'
        // 
        // vTV.SortArray; // <-- Sort before nesting the list into vMain
        // 
        vMain._('TaggedValues').Add( vTV );
    end;

By producing consistent locations of objects I will be able to track any changes in model info (using diff), and for this reason I happily "take the pain" to handle each list/array individually in order to be able to sort them.

// Rolf Lampa

Last edited by RIL (2015-02-15 09:12:25)

Offline

#4 2015-02-15 09:07:34

RIL
Member
Registered: 2015-02-15
Posts: 4

Re: Sorting content in Variants & TDocVariant ?

ab wrote:

I've added the TDocVariantData.SortByName method.

Would something similar be possible also for TDocVariant? (All my existing code uses TDocVariant). I'm a bit lost on these variants still. smile

Btw, it seems like the "Exchg" procedure was redeclared (patching only this "SortByName", "Compare" & "Exchg" won't compile).

// Rolf Lampa

Offline

#5 2015-02-15 09:29:06

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,609
Website

Re: Sorting content in Variants & TDocVariant ?

TDocVariantData is the wrapper around a TDocVariant variant instance.

If you are sure that a variant is a TDocVariant, you can just use TDocVariantData(aVariant).SortByName.
Or use DocVariantSafe(aVariant).SortByName which will first check that aVariant is a TDocVariant.

The Exchg() function was taken from some code below, and somewhat modified (from max to count parameter).
You need to retrieve the whole SynCommons.pas unit to let it work.

Offline

#6 2015-02-15 10:48:47

RIL
Member
Registered: 2015-02-15
Posts: 4

Re: Sorting content in Variants & TDocVariant ?

Ah, the wrapper makes sense. It would be useful to add this info to the documentation on this page: http://synopse.info/files/html/Synopse% … ml#TITL_80

The examples are very good, but finding out how to add data as Pairs, Objects and Arrays (both variants and strings added to existing variants) took much reading and trying (tiiime) for a newbie. smile

Offline

#7 2015-02-15 11:11:09

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,609
Website

Re: Sorting content in Variants & TDocVariant ?

AFAIK it was already there.

Documentation wrote:

You may also trans-type your variant instance into a TDocVariantData record, and access directly to its internals.
For instance:

TDocVariantData(V1).AddValue('comment','Nice guy');
with TDocVariantData(V1) do             // direct transtyping
   if Kind=dvObject then                 // direct access to the TDocVariantKind field
   for i := 0 to Count-1 do              // direct access to the Count: integer field
     writeln(Names[i ],'=',Values[i ]);    // direct access to the internal storage arrays

By definition, trans-typing via a TDocVariantData record is slightly faster than using late-binding.

But you must ensure that the variant instance is really a TDocVariant kind of data before transtyping e.g. by calling DocVariantType.IsOfType(aVariant) or the DocVariantData(aVariant)^ or DocVariantDataSafe(aVariant)^ functions, which works even for members returned as varByRef via late binding:

if DocVariantType.IsOfType(V1) then
with TDocVariantData(V1) do             // direct transtyping
   for i := 0 to Count-1 do              // direct access to the Count: integer field
     writeln(Names[i ],'=',Values[i ]);    // direct access to the internal storage arrays


writeln(V2.doc); // will write  '{"name":"john","doc":{"one":1,"two":2.5}}'
if DocVariantType.IsOfType(V2.Doc) then // will be false, since V2.Doc is a varByRef variant
   writeln('never run');                 // .. so TDocVariantData(V2.doc) will fail
with DocVariantData(V2.Doc)^ do         // note ^ to de-reference into TDocVariantData
   for i := 0 to Count-1 do              // direct access the TDocVariantData methods
     writeln(Names[i ],'=',Values[i ]);
  // will write to the console:
  //  one=1
  //  two=2.5

Offline

Board footer

Powered by FluxBB