用TRewinReaderProc回绕TJSONIterator类的输入数据 [英] Rewinding input data of TJSONIterator class with TRewindReaderProc

查看:0
本文介绍了用TRewinReaderProc回绕TJSONIterator类的输入数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在用Embarcadero的C++Builder(东京10.2更新3)构建的程序中解析JSON,考虑到它们严重缺乏文档,这并不容易。

我使用的是TJSONIteratorFind方法,如果您给出的路径(例如[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

这篇关于用TRewinReaderProc回绕TJSONIterator类的输入数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆