如何在Delphi中正确解析Shopify的JSON响应(含空值处理)
解决Shopify JSON解析的Delphi代码优化问题
问题背景
需要解析Shopify返回的JSON响应,提取其中id和price字段,但存在以下难点:
variant[n]是动态命名的键(如variant0、variant1),并非JSON数组,数量未知- 部分
variant[n]下的productVariant为null,原代码遇到null会触发异常,导致循环提前终止
优化方案
1. 替换异常捕获,主动检查节点状态
原代码依赖try-except捕获异常判断节点存在性,不仅性能差,还无法区分“节点不存在”和“节点为null”的情况。改为逐层检查:
- 先获取
data对象 - 检查目标
variant节点是否存在 - 若存在,再确认
productVariant不为null - 最后安全提取
id和price
2. 两种可靠的遍历方式
方式一:按序号循环检查(适配连续命名场景)
适合确认variant是连续序号命名的情况,循环检查每个variant[i],直到找不到为止。
方式二:遍历data所有属性(适配任意命名场景)
直接遍历data对象的所有键,筛选出以variant开头的键,无需依赖序号连续性,兼容性更强。
优化后的代码示例
方式一:按序号循环检查
procedure TfrmMain.Button20Click(Sender: TObject); var JsonValue: TJSONValue; DataObj: TJSONObject; VariantObj: TJSONObject; ProductVarObj: TJSONValue; i: Integer; resp: string; id, price: string; begin resp := mmInputs.Lines.Text; JsonValue := TJSONObject.ParseJSONValue(resp); try if JsonValue is TJSONObject then begin DataObj := JsonValue.GetValue<TJSONObject>('data'); if Assigned(DataObj) then begin i := 0; while True do begin // 检查当前variant[i]是否存在 VariantObj := DataObj.GetValue<TJSONObject>('variant' + IntToStr(i)); if not Assigned(VariantObj) then Break; // 无更多variant,退出循环 // 检查productVariant是否非空 ProductVarObj := VariantObj.GetValue('productVariant'); if Assigned(ProductVarObj) and (ProductVarObj.ValueType <> jvtNull) then begin id := ProductVarObj.GetValue<string>('id'); price := ProductVarObj.GetValue<string>('price'); Memo1.Lines.Add('variant' + IntToStr(i) + ' id=' + id + ' price=' + price); end; Inc(i); end; end; // 提取剩余请求点数 Memo1.Lines.Add('pointsRemaining:' + JsonValue.GetValue<string>('extensions.cost.throttleStatus.currentlyAvailable')); end; finally JsonValue.Free; end; end;
方式二:遍历data的所有属性
procedure TfrmMain.Button20Click(Sender: TObject); var JsonValue: TJSONValue; DataObj: TJSONObject; Pair: TJSONPair; VariantObj: TJSONObject; ProductVarObj: TJSONValue; resp: string; id, price: string; begin resp := mmInputs.Lines.Text; JsonValue := TJSONObject.ParseJSONValue(resp); try if JsonValue is TJSONObject then begin DataObj := JsonValue.GetValue<TJSONObject>('data'); if Assigned(DataObj) then begin // 遍历data下所有键值对 for Pair in DataObj do begin // 筛选variant开头的键 if StartsText('variant', Pair.JsonString.Value) then begin VariantObj := Pair.JsonValue as TJSONObject; if Assigned(VariantObj) then begin ProductVarObj := VariantObj.GetValue('productVariant'); if Assigned(ProductVarObj) and (ProductVarObj.ValueType <> jvtNull) then begin id := ProductVarObj.GetValue<string>('id'); price := ProductVarObj.GetValue<string>('price'); Memo1.Lines.Add(Pair.JsonString.Value + ' id=' + id + ' price=' + price); end; end; end; end; end; Memo1.Lines.Add('pointsRemaining:' + JsonValue.GetValue<string>('extensions.cost.throttleStatus.currentlyAvailable')); end; finally JsonValue.Free; end; end;
关键改进点
- 主动检查节点存在性和非空状态,替代异常捕获,提升性能和准确性
- 两种遍历方式覆盖不同场景:连续序号用方式一,任意命名用方式二
- 避免因单个
null节点导致整个循环终止
内容的提问来源于stack exchange,提问作者Joao
相关产品推荐
相关产品推荐

