如何在Delphi中使用TJSONIterator备份多个级别?

问题描述 投票:4回答:3

我一直试图在Delphi 10.2.2中使用TJSONIterator。简短的问题是“如何在Iterator上升两个级别?”

以下代码说明了我的问题:

JsonRec := '{"v1":"Main","v2":"1.1","v3":{"id":"X45","mod":1.5,"r2":{"rv1":"99190","rv2":"TX"}},"v4":"ok","v5":69}';

PDS.Open;
PDS.Append;
StringReader := TStringReader.Create(JsonRec);
JsonTextReader := TJsonTextReader.Create(StringReader);
Iterator := TJSONIterator.Create(JsonTextReader);
If Iterator.Next('v1') Then
   PDS['Type'] := Iterator.AsString;
If Iterator.Next('v2') Then
   PDS['Version'] := Iterator.AsString;
If Iterator.Next('v3') Then
   Begin
   Iterator.Recurse;
   If Iterator.Next('id') Then
      PDS['BlackListInfo'] := Iterator.AsString;
   If Iterator.Next('mod') Then
      PDS['Speed'] := Iterator.AsDouble;
   If Iterator.Next('r2') Then
      begin
      Iterator.Recurse;
      if Iterator.Next('rv1') then
         PDS['Serial'] := Iterator.AsString;
      if Iterator.Next('rv2') then
         PDS['Location'] := Iterator.AsString;
      Iterator.Return;
      end;
   Iterator.Return;   //Second Return does not go up a level.
   if Iterator.Next('v4') then // Always fails
      PDS['CRC'] := Iterator.AsString;
   if Iterator.Next('v5') then
      PDS['ReportID'] := Iterator.AsInteger;
   PDS.Post;
   End;

显然,我正在解析JSON字符串以将数据放入数据库(PDS)。当我发出第二次回报时,我没有达到预期的水平,然后我找不到v4。我怀疑我可能需要使用Rewind方法,但到目前为止我一直无法找到它的文档。

任何帮助是极大的赞赏。

json delphi delphi-10.2-tokyo
3个回答
1
投票

这个答案解释了如何实现你想要做的事情,但是没有使用TJsonReader,它是为delphi制作的更糟糕的json解析器(性能和可用性)之一。 (您可以使用此工具制作基准:https://svn.code.sf.net/p/alcinoe/code/demos/ALJsonDoc/win32/AljsonDocDemo.exe

使用例如Alcinoe(https://github.com/Zeus64/alcinoe)代码非常简单(但任何其他json解析器也可以很好地完成这种工作)

MyJsonDoc := TalJsonDocumentU.create;
try
  MyJsonDoc.loadFromJsonString('{"v1":"Main","v2":"1.1","v3":{"id":"X45","mod":1.5,"r2":{"rv1":"99190","rv2":"TX"}},"v4":"ok","v5":69}');
  PDS['Type'] := MyJsonDoc.node.getchildNodeValueText('v1', ''{default});
  PDS['Version'] := MyJsonDoc.node.getchildNodeValueText('v2', ''{default});
  PDS['BlackListInfo'] := MyJsonDoc.node.getchildNodeValueText(['v3', 'id'], ''{default});
  PDS['Speed'] := MyJsonDoc.node.getchildNodeValueFloat(['v3', 'mod'], 0{default});
  PDS['Serial'] := MyJsonDoc.node.getchildNodeValueText(['v3', 'r2', 'rv1'], ''{default});
  PDS['Location'] := MyJsonDoc.node.getchildNodeValueText(['v3', 'r2', 'rv2'], ''{default});
  PDS['CRC'] := MyJsonDoc.node.getchildNodeValueText('v4', ''{default});
  PDS['ReportID'] := MyJsonDoc.node.getchildNodeValueInt32('v5', 0{default});
finally
  MyJsonDoc.free;
end;

1
投票

简短的回答

Iterator.Next电话之间使用Iterator.Return。如果您不想在该级别处理任何内容,请为空。

只需替换这部分代码即可

  if Iterator.Next('rv2') then
     PDS['Location'] := Iterator.AsString;
  Iterator.Return;
  end;

有了这个

  if Iterator.Next('rv2') then
     PDS['Location'] := Iterator.AsString;
  Iterator.Return;
  Iterator.Next;
  end;

答案很长

我不确定这是一个bug还是它的意图和文档根本没有帮助,但Return只适用于一个级别。如果你看一下inslemntation,你可以看到,Return只会将读者移动到第一个结束标记,减少深度,并保持在那里,如果它还没有。

在这种情况下,Return的第一次调用移动到r2的末尾并减少深度,下一​​个调用将什么也不做,因为它已经在结束并且不满足not FReader.IsEndToken (FReader.TokenType)条件。

Iterator.Next('v4')移动到v3的末尾并将深度减少到1,但也将私人变量FFinished设置为True,这将导致Next的所有其他调用将不会做任何事情,因为if FFinished then Exit开始时的条件。重置FFinish的唯一方法是使用ReturnRewind


0
投票

我正在寻找一个关于如何使用TJSONIterator并降落在这里的例子。由于几乎找不到任何东西,我想我会分享我想出的解决方案:

...
  ecDebug: TMemo;
...
uses
  System.JSON.Builders,
  System.JSON.Readers,
...
procedure TForm1.Button1Click(Sender: TObject);
const
  JsonRec = '{"v1":"Main","v2":"1.1","v3":{"id":"X45","mod":1.5,' +
    '"r2":{"rv1":"99190","rv2":"TX"}},"v4":"ok","v5":69}';
var
  StringReader: TStringReader;
  JsonTextReader: TJsonTextReader;
  Iterator: TJSONIterator;
begin
  JsonTextReader:= nil;
  Iterator:= nil;
  StringReader:= TStringReader.Create(JsonRec);
  try
    JsonTextReader:= TJsonTextReader.Create(StringReader);
    Iterator:= TJSONIterator.Create(JsonTextReader);
    while Iterator.Next do
    begin
      if Iterator.Key = 'v1' then
        ecDebug.Lines.Add(Format('Type = %s', [Iterator.AsString]))
      else if Iterator.Key = 'v2' then
        ecDebug.Lines.Add(Format('Version = %s', [Iterator.AsString]))
      else if Iterator.Key = 'v3' then
      begin
        Iterator.Recurse;
        while Iterator.Next do
        begin
           if Iterator.Key = 'id' then
             ecDebug.Lines.Add(Format('BlackListInfo = %s', [Iterator.AsString]))
           else if Iterator.Key = 'mod' then
             ecDebug.Lines.Add(Format('Speed = %g', [Iterator.AsDouble]))
           else if Iterator.Key = 'r2' then
           begin
             Iterator.Recurse;
             while Iterator.Next do
             begin
               if Iterator.Key = 'rv1' then
                 ecDebug.Lines.Add(Format('Serial = %s', [Iterator.AsString])) 
               else if Iterator.Key = 'rv2' then
                 ecDebug.Lines.Add(Format('Location = %s', [Iterator.AsString]));
             end;
             Iterator.Return;
           end;
        end;
        Iterator.Return;
      end
      else if Iterator.Key = 'v4' then
        ecDebug.Lines.Add(Format('CRC = %s', [Iterator.AsString]))
      else if Iterator.Key = 'v5' then
        ecDebug.Lines.Add(Format('ReportID = %d', [Iterator.AsInteger]));
    end;
  finally
    Iterator.Free;
    JsonTextReader.Free;
    StringReader.Free;
  end;
end;

我相信这是TJSONIterator打算使用的方式。此代码不依赖于JSON元素的顺序。

备忘录中的输出是:

Type = Main
Version = 1.1
BlackListInfo = X45
Speed = 1,5
Serial = 99190
Location = TX
CRC = ok
ReportID = 69
© www.soinside.com 2019 - 2024. All rights reserved.