我一直试图在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
方法,但到目前为止我一直无法找到它的文档。
任何帮助是极大的赞赏。
这个答案解释了如何实现你想要做的事情,但是没有使用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;
简短的回答
在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
的唯一方法是使用Return
或Rewind
。
我正在寻找一个关于如何使用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