You are not logged in.
Pages: 1
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
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
I've added the TDocVariantData.SortByName method.
See http://synopse.info/fossil/info/b38f4de17dYour 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
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.
Btw, it seems like the "Exchg" procedure was redeclared (patching only this "SortByName", "Compare" & "Exchg" won't compile).
// Rolf Lampa
Offline
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
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.
Offline
AFAIK it was already there.
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 arraysBy 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 arrayswriteln(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
Pages: 1