如何解决TJSONIterator与string类型不兼容问题及JSON遍历方法
问题描述
我使用RAD Studio的JSON读写框架(可降低内存消耗),尝试通过HTTP调用RESTful服务获取数据,但出现编译错误:
E2010 Incompatible types: 'TJSONIterator' and 'string'
错误出现在这行代码:
LJRates := LJIter.AsDouble.ToString;
我的代码如下:
Async.Run<TStringList>(function: TStringList var LHTTP: THTTPClient; LResp: IHTTPResponse; LJIter: TJSONIterator; LJRates: TJSONIterator; LJTextR: TJsonTextReader; LStrR: TStringReader; // LResult: Boolean; I: Integer; begin LHTTP := THTTPClient.Create; try LResp := LHTTP.Get('http://api.fixer.io/latest'); {$REGION 'check for errors'} if LResp.StatusCode = 200 then begin // LResult := not LResp.ContentAsString(TEncoding.UTF8).IsEmpty // or // LStrR := TJSONObject.ParseJSONValue(LResp.ContentAsString(TEncoding.UTF8)) as TJSONObject; // or // LStrR := LResp.ContentAsString(TEncoding.UTF8); // or LStrR := TStringReader.Create(LResp.ContentAsString(TEncoding.UTF8)); end else begin raise Exception.CreateFmt('Cannot get rates. HTTP %d - %s', [LResp.StatusCode, LResp.StatusText]); end; {$ENDREGION} LJTextR := TJsonTextReader.Create(LStrR); LJIter := TJSONIterator.Create(LJTextR); try // gets the json object 'rates' and LJIter.Recurse; // prepare to enter object while LJIter.Next do; begin LJIter.Recurse; // enter object LJIter.Next('rates'); LJRates := LJIter.AsDouble.ToString; Result := TStringList.Create; // loop through the property names for I := 0 to LJRates.Depth - 1 do begin //add each names in the resulting TStringList Result.Add(LJRates.Path[I]); end; Result.Sort; end; finally LJIter.Free; end; finally LHTTP.Free; end; end,
请问正确遍历JSON、读取内容并显示汇率值的方法是什么?
解决方案
错误根源
你声明LJRates为TJSONIterator类型,但代码里试图把LJIter.AsDouble.ToString()的字符串结果赋值给它,类型完全不匹配,这就是编译错误的核心原因。同时你的遍历逻辑也存在问题:比如while LJIter.Next do;后面多了分号,导致后续代码块不会循环执行;定位rates对象的步骤也不正确。
修正后的代码
以下是正确遍历JSON并提取汇率名称与对应数值的代码:
Async.Run<TStringList>(function: TStringList var LHTTP: THTTPClient; LResp: IHTTPResponse; LJIter: TJSONIterator; LJTextR: TJsonTextReader; LStrR: TStringReader; begin LHTTP := THTTPClient.Create; try LResp := LHTTP.Get('http://api.fixer.io/latest'); if LResp.StatusCode <> 200 then raise Exception.CreateFmt('Cannot get rates. HTTP %d - %s', [LResp.StatusCode, LResp.StatusText]); LStrR := TStringReader.Create(LResp.ContentAsString(TEncoding.UTF8)); try LJTextR := TJsonTextReader.Create(LStrR); try LJIter := TJSONIterator.Create(LJTextR); try Result := TStringList.Create; // 定位到JSON根对象 if LJIter.Next and (LJIter.Kind = TJsonToken.StartObject) then begin // 进入根对象 LJIter.Recurse; // 查找"rates"字段 while LJIter.Next do begin if LJIter.Name = 'rates' then begin // 进入rates对象 LJIter.Recurse; // 遍历rates下的所有汇率项 while LJIter.Next do begin if LJIter.Kind = TJsonToken.PropertyName then begin // 记录汇率名称 var RateName := LJIter.Value.AsString; // 移动到对应的汇率数值节点 LJIter.Next; // 将名称和值格式化后添加到结果列表 Result.Add(Format('%s: %.4f', [RateName, LJIter.AsDouble])); end; end; // 退出rates对象 LJIter.Return; break; end; end; // 退出根对象 LJIter.Return; end; Result.Sort; finally LJIter.Free; end; finally LJTextR.Free; end; finally LStrR.Free; end; finally LHTTP.Free; end; end);
关键说明
- 资源管理:补充了
LStrR和LJTextR的释放操作,避免内存泄漏; - 遍历逻辑优化:
- 先定位JSON根对象,再精准查找
rates字段; - 进入
rates对象后,遍历每个属性名(汇率名称),再移动到对应的数值节点读取值; - 使用
Recurse进入对象、Return退出对象,保证迭代器的层级始终正确;
- 先定位JSON根对象,再精准查找
- 类型匹配修正:不再混淆迭代器类型与字符串类型,直接读取所需字段值并格式化输出。
内容的提问来源于stack exchange,提问作者LegitLearner
相关产品推荐
相关产品推荐

