我正在尝试在使用embarcadero的c ++ builder(Tokyo 10.2 Update 3)构建的程序中解析JSON,考虑到他们严重缺乏文档,这并不容易。
如果您提供的路径(例如TJSONIterator
或Find
)存在于JSON数据中,我使用[0]['key']
car.model['colour']
方法返回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
,但我不认为这是因为文档中关于{的唯一内容{1}}是
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.
读取JSON数据,该数据被提供给TStringReader
类构造函数,并且被提供给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
方法应该采用什么?
答案 0 :(得分:0)
我在Delphi中遇到了同样的问题几个小时,最后我设法找到一种方法让它工作。我使用TFileStream和TStreamReader作为底层读者而不是TStringReader,但我希望你理解任何读者背后的概念。这是我的代码:
procedure LoadFromFile(AFileName: string);
var
FS: TFileStream;
StreamReader: TStreamReader;
JTReader: TJsonTextReader;
JIterator: TJSONIterator;
RewindProc: TJSONIterator.TRewindReaderProc;
begin
RewindProc := procedure (AReader: TJsonReader)
begin
TStreamReader(TJsonTextReader(AReader).Reader).DiscardBufferedData;
TStreamReader(TJsonTextReader(AReader).Reader).BaseStream.Seek(0, TSeekOrigin.soBeginning);
end;
FS:=TFileStream.Create(AFileName, fmOpenRead);
StreamReader:=TStreamReader.Create(FS);
JTReader:=TJsonTextReader.Create(StreamReader);
JIterator:=TJSONIterator.Create(JTReader , RewindProc);
JIterator.Find('some.path.here');
JIterator.Find('other.path.here');
end;
RewindProc
程序中的线条变得神奇。正如文档所说,你必须对基础读者进行回放,这就是我对这两行所做的。第一个清理StreamReader的内部缓冲区,第二个将文件指针移动到开头。
我希望这会以某种方式帮助您,因为文档非常糟糕,我无法在Internet上找到有关如何正确设置要传递给JSON迭代器的回滚过程的文章。
PD:然后我发现了一些与TIterator.Find
一起工作的其他麻烦,但这是另一回事,所以我专注于你的具体问题。
答案 1 :(得分:0)
由于TJSONIterator
已经在阅读器上调用了Rewind
,因此在调用您的倒带过程之前,无需再次调用倒带。而是重置流并丢弃Reader的所有缓冲区:
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添加了必要的调用。只有在使用更大的文件进行测试之后,这种情况才会出现。
Update2:对于大于1K的json文件,有一种解决方法,需要解决TJsonTextReader中的错误,该错误无法清除FChar,因此在调用.Rewind导致异常后,它不会重新读取json文件。解析值...时遇到意外字符。”。要访问私有FChar,我正在使用https://stackoverflow.com/a/36717896/386473中所述的RTTI。该错误以https://quality.embarcadero.com/browse/RSP-24517的身份记录在QP中。