#1 2016-11-09 17:58:07

KeithG
Member
From: France
Registered: 2016-10-15
Posts: 11

Add a value to JSON

I have a variant which is a TDocVariant.  I need to add a value to it.  I tried this:

DocVariantData(myvariant).AddValue(FieldName,0);

However when I then look at myvariant in the debugger,  it doesn't contain my new field.  If I assign DocVariantData(myVariant) to a PDocVariantData,  and then do the AddValue call on that,  the DocVariantData varaible contains the value,  but the TDocVariant doesn't.

Why not,  and what do I have to do?

(It seems much much harder than it ought to be to update values in JSON - Are there not simple examples somewhere that show how this sort of thing should be done).

Regards
Keith.

Offline

#2 2016-11-09 18:08:11

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

Re: Add a value to JSON

Either

myvariant.fieldname := 0;

or

  _Safe(myvariant)^.AddValue(FieldName,0);

... how do you create your variant?
Please show some reproducible code, and check again the TDocVariant documentation.

Offline

#3 2016-11-10 10:23:30

KeithG
Member
From: France
Registered: 2016-10-15
Posts: 11

Re: Add a value to JSON

The variant is pulled from an array of json data that comes back from a MongoDB query.  The query is returned as a string and is then treated as follows:

lResult := _JSon(RawUTF8(lDoc), [dvoJSONParseDoNotTryCustomVariants]);
for lRecord := 0 to TDocVariantData(lResult).Count - 1 do
begin
   v := TDocVariantData(lResult).Value[lRecord];
   _UniqueFast(v);
   Do some processing(v);
end;


The code that is processing v receives v as a value type (ie not passed by ref), it basically stores the variant away as part of a record:

lSPMRow.Content := v;  // content is a variant.

The data from the variant is displayed in a virtual tree view grid.  However not all fields exist in all records,  and as the records are editable,  if a user tries to edit a field that doesn't have data,  we need to add it to the variant,  and post it back to Mongo.  Everything I read implied that you cast the variant to a TDocVariantData,  add the field,  at which point it should then be in the original variant,  but this isn't the case.  For editing an existing field,  it seems to work:

vd is of type PDocVariantData.

      vd := DocVariantData(lDisplayObject.Content[lRow]);
      lIndex := vd.GetValueIndex(RawUTF8(lTagName));

      vd.Values[lIndex] := Newtext;  // for string data

       TDocVariant.New(lDate);
       TDocVariantData(lDate).Value['$date'] := NewText;
       vd.Values[lIndex] := lDate;   // For date time values

       vd.Values[lIndex] := TFixedConv.Str2Double(NewText);  // for numbers

Once the value is written,  using the above,  lDisplayObject.Content[lRow] has the new value.

However if I used AddValue to add the new field, lDisplayObject.Content[lRow] doesn't have the new field.  For me this feels inconsistent as I'd expect it to work the same way in both cases,  which is why I'm confused :-).

I'll try your suggestion now and let you know if it works. 

Incidentally I had read all the relevant docs,  but I find that it isn't very clear how to proceed in the situation above.  It seems more about creating new variants.  It also seems to suppose you know the fieldnames, rather than our case where the field names are not known, and everything has to be referred to via string parameters.  ie,  I can't write v.Name to update a field called name,  I have to pass the FieldName parameter which contains the value 'Name'.

Thanks for your help

Regards
Keith

Offline

#4 2016-11-10 10:34:53

KeithG
Member
From: France
Registered: 2016-10-15
Posts: 11

Re: Add a value to JSON

Using your suggestion of _Safe doesn't put the new value into the variant either.

I should perhaps explain that better: 

I do this:
        _Safe(DisplayObject.Content[lRow])^.AddValue(lTagName,0)

where lTagName is 'DayUreaFactor'

If I then look at DisplayObject[lRow] in the debugger,  immediately after executing that line,  it doesn't contain a field 'DayUreaFactor'.  Similarly if I try to get the index of the field to update it later with

      vd := DocVariantData(DisplayObject.Content[lRow]);
      lIndex := vd.GetValueIndex(RawUTF8(lTagName));

it returns -1.

Regards
Keith

Last edited by KeithG (2016-11-10 10:51:06)

Offline

#5 2016-11-10 16:25:29

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

Re: Add a value to JSON

What is a DisplayObject?
If it returns a copy of the variant the original is not modified.

We do not understand what you are doing....

Offline

#6 2016-11-10 21:41:38

KeithG
Member
From: France
Registered: 2016-10-15
Posts: 11

Re: Add a value to JSON

lDisplayObject is not really relevant.  It's property Content is a variant that provides access to the field fContent which is where we store a record from the MongoDB query result.  ie the getter code is Result := fContent;  and the setter is FContent := Value;  It seems to me that you are saying that if my variant is accessed via a property,  it will never get updated?  If that is the case,  what would I need to do in order to have a property that did get updated?

Regards
Keith

Offline

#7 2016-11-10 21:49:31

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

Re: Add a value to JSON

Check the doc and ensure your variant will be copied by reference.
E.g use _jsonfast().

Offline

#8 2016-11-10 21:59:10

KeithG
Member
From: France
Registered: 2016-10-15
Posts: 11

Re: Add a value to JSON

ok,  can you comment on whether this will work? 

type
  TSPMQueryData = class(TInterfacedObject,ISPMQueryData)
  private
    // Holds the results of the query
    FReportData : IList<variant>;
    FReportColumns : IList<IDisplayConfigurationField>;

    function GetHasData: boolean;
    function GetRow(index : integer): variant;
    function GetCol(row: integer; col : string): variant;
    procedure SetCol(row: integer; col : string; const Value: variant);
  public
    constructor Create;
    procedure AddRow(ReportResults : RawUtf8);
    property HasData : boolean read GetHasData;
    property Row[index : integer] : variant read GetRow;
    property Column[row : integer; col : string] : variant read GetCol write SetCol;
  end;

implementation

{ TSPMQueryData }

procedure TSPMQueryData.AddRow(ReportResults: RawUtf8);
begin
  FReportData.Add(_JSonFast(RawUTF8(ReportResults), [dvoJSONParseDoNotTryCustomVariants]));
end;

constructor TSPMQueryData.Create;
begin
  FReportData := TCollections.CreateList<variant>;
end;

function TSPMQueryData.GetCol(row: integer; col: string): variant;
var
  lrow : variant;
begin
  lrow := FReportData[row];
  result := TDocVariantData(lRow).Value[col];
end;

function TSPMQueryData.GetHasData: boolean;
begin
  Result := FReportData.Count <> 0;
end;

function TSPMQueryData.GetRow(index : integer): variant;
begin
  Result := FReportData[index];
end;

procedure TSPMQueryData.SetCol(row: integer; col: string; const Value: variant);
var
  lrow : variant;
begin
  lrow := FReportData[row];
  TDocVariantData(lRow).Value[col] := Value;
end;

Basically the IList<variant> is a list of TDocVariant records from a MongoDB query,  and I want to be able to update it (or add a column)  by setting the Column property. 

Regards
Keith

Last edited by KeithG (2016-11-10 21:59:42)

Offline

#9 2016-11-11 13:03:36

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

Re: Add a value to JSON

How do you create the variants in this list?

Offline

#10 2016-11-12 10:09:14

KeithG
Member
From: France
Registered: 2016-10-15
Posts: 11

Re: Add a value to JSON

They are created in the AddRow call,  passed in as a UTF8 JSON/BSON string

Regards
Keith.

Offline

Board footer

Powered by FluxBB