用TRewinReaderProc回绕TJSONIterator类的输入数据
我正在尝试在用Embarcadero的C++Builder(东京10.2更新3)构建的程序中解析JSON,考虑到它们严重缺乏文档,这并不容易。
我使用的是TJSONIterator
Find
方法,如果您给出的路径(例如[0]['key']
或car.model['colour']
)存在于JSON数据中,则该方法返回TRUE或FALSE,根据Embarcadero的文档,它需要一个回绕过程传递给TJSONIterator
类的构造函数,如果不存在,则抛出一个异常来说明这一点。
回绕过程应继承_di_TRewindReaderProc
接口,因此这是我的类。
class rewindclass : public TJSONIterator::_di_TRewindReaderProc
{
public:
void __fastcall Invoke(System::Json::Readers::TJsonReader* AReader)
{
//code to rewind Iterator
Areader->Rewind();
}
};
我不确定Invoke
函数应该包含什么内容,因为正如我所说的,文档毫无用处。显然,您必须对已传递的TJsonReader
做一些操作,我看到的唯一可以使用的函数是Rewind
,但我认为不是这样,因为文档中关于TRewindReaderProc
的唯一说明是
Reference to a procedure that rewinds the input data of the specified JSON reader.
Note: TJsonReader.Rewind does not rewind the input data, it resets the state of the JSON
reader. This procedure must rewind the actual data stream that provides the input data
of the JSON reader.
我看不出还有什么可以替代的。它说必须重置提供输入的实际数据流,但我不确定如何执行此操作。
我使用TStringReader
读取JSON数据,该数据被提供给TJsonTextReader
类构造函数,并被提供给TJSONIterator
类构造函数和使用_di_TRewindReaderProc
接口的类。
//create rewindclass
rewindclass *rewind = new rewindclass();
//setting up TJSONIterator class
TStringReader *sread = new TStringReader(this->Memo1->Text);
TJsonTextReader *jread = new TJsonTextReader(sread);
TJSONIterator *jit = new TJSONIterator(jread, *rewind);
这段代码编译得很好,但当我调试它并步入TJSONIterator
构造函数时,TJsonTextReader
没有传递,因此当我第二次调用Find
方法时,它抛出一个异常,说没有设置回调过程。
有谁知道_di_TRewindReaderProc
没有被传递的原因,以及Invoke
方法中应该包含什么内容?
解决方案
因为TJSONIterator
已经在读取器上调用了Rewind
,所以在它调用您的倒带过程之前,没有必要再次调用ReWind。相反,重置流并丢弃读取器的所有缓冲区:
procedure TForm1.Button1Click(Sender: TObject);
const
JsonRec = '{"some":{"path":{"there":"ahi", "here":"acqui"}}}';
var
StringStream: TStringStream;
StreamReader: TStreamReader;
JsonTextReader: TJsonTextReader;
Iterator: TJSONIterator;
begin
JsonTextReader:= nil;
Iterator:= nil;
StringStream:= TStringStream.Create(JsonRec);
try
StreamReader:= TStreamReader.Create(StringStream);
JsonTextReader:= TJsonTextReader.Create(StreamReader);
Iterator:= TJSONIterator.Create(JsonTextReader,
procedure (AReader: TJSONReader)
var
v: TValue;
begin
StringStream.Seek(0, soBeginning);
StreamReader.DiscardBufferedData;
//workaround for RSP-24517
v:= TRttiContext.Create.GetType(TJsonTextReader).GetField('FChars').GetValue(AReader);
v.SetArrayElement(0, #0);
end);
if Iterator.Find('some.path.here') then
ecDebug.Lines.Add(Iterator.AsString);
if Iterator.Find('some.path.there') then
ecDebug.Lines.Add(Iterator.AsString);
finally
Iterator.Free;
JsonTextReader.Free;
StreamReader.Free;
StringStream.Free;
end;
end;
似乎没有重置TStringReader
的方法,但这就是我改用TStringStream
的原因。
更新:我已经在回绕过程中添加了对DiscardBufferedData的必要调用。这仅在使用较大文件进行测试后才会出现。
更新2:对于大于1K的json文件,需要解决TJsonTextReader中的一个错误,该错误无法清除FChar,因此它不会在调用.Reind后重新读取json文件,这会导致异常"在解析值时遇到意外字符..."。要访问私有FChar,我使用https://stackoverflow.com/a/36717896/386473中所述的RTTI。该错误在QP中记录为https://quality.embarcadero.com/browse/RSP-24517。相关文章