如何在Delphi XE2中解析嵌套的JSON对象?

15

我对JSON不太了解,手头上有一个项目需要我解析JSON并在ListView中显示一些内容。问题是我读到的文档都是关于JSON对象包含JSON数组的,而我的情况涉及处理嵌套的对象。简而言之,这是摘要:我正在使用带有DBXJSON的Delphi XE2。我向服务器发送一些值,它回复一个看起来像这样的JSON对象:

    {
    "products": {
        "Men's Sneakers": {
            "instock": false,
            "size": "423",
            "manufacturer": "Adidas",
            "lastcheck": "20120529"
        },
        "Purse": {
            "instock": true,
            "size": "not applicable",
            "manufacturer": "Prada",
            "lastcheck": "20120528"
        },
        "Men's Hood": {
            "instock": false,
            "size": "M",
            "manufacturer": "Generic",
            "lastcheck": "20120529"
       }
    },
   "total": 41,
   "available": 30
}

我的目标是将每个项(如钱包)解析并添加为列表视图中的标题,以及一个子项(制造商)。我创建了一个使用JSON字符串作为参数的过程,创建了JSON对象,但不知道如何进一步解析嵌套对象。

procedure TForm1.ParseString(const AString: string);
var
  json          : TJSONObject;
  jPair         : TJSONPair;
  jValue        : TJSONValue;
  jcValue       : TJSONValue;
  l,i           : Integer;
begin
    json    := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(AString),0) as TJSONObject;
  try
    //get the pair to evaluate in this case the index is 1
    jPair   := json.Get(1); 
        {further process the nested objects and adding them to the listview}
  finally
     json.Free;
  end;
end;

非常感谢任何建议。我已经花了很多时间尝试在Delphi中理解JSON的细节,但没有成功。

谢谢, sphynx

3个回答

30

尝试这个示例

{$APPTYPE CONSOLE}

{$R *.res}

uses
  DBXJSON,
  System.SysUtils;


Const
StrJson=
'{'+
'    "products": {'+
'        "Men''s Sneakers": {'+
'            "instock": false,'+
'            "size": "423",'+
'            "manufacturer": "Adidas",'+
'            "lastcheck": "20120529"'+
'        },'+
'        "Purse": {'+
'            "instock": true,'+
'            "size": "not applicable",'+
'            "manufacturer": "Prada",'+
'            "lastcheck": "20120528"'+
'        },'+
'        "Men''s Hood": {'+
'            "instock": false,'+
'            "size": "M",'+
'            "manufacturer": "Generic",'+
'            "lastcheck": "20120529"'+
'        }'+
'    },'+
'    "total": 41,'+
'    "available": 30'+
'}';

procedure ParseJson;
var
  LJsonObj  : TJSONObject;
  LJPair    : TJSONPair;
  LProducts : TJSONValue;
  LProduct  : TJSONValue;
  LItem     : TJSONValue;
  LIndex    : Integer;
  LSize     : Integer;
begin
    LJsonObj    := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(StrJson),0) as TJSONObject;
  try
     LProducts:=LJsonObj.Get('products').JsonValue;
     LSize:=TJSONArray(LProducts).Size;
     for LIndex:=0 to LSize-1 do
     begin
      LProduct := TJSONArray(LProducts).Get(LIndex);
      LJPair   := TJSONPair(LProduct);
      Writeln(Format('Product Name %s',[LJPair.JsonString.Value]));
        for LItem in TJSONArray(LJPair.JsonValue) do
        begin
           if TJSONPair(LItem).JsonValue is TJSONFalse then
            Writeln(Format('  %s : %s',[TJSONPair(LItem).JsonString.Value, 'false']))
           else
           if TJSONPair(LItem).JsonValue is TJSONTrue then
            Writeln(Format('  %s : %s',[TJSONPair(LItem).JsonString.Value, 'true']))
           else
            Writeln(Format('  %s : %s',[TJSONPair(LItem).JsonString.Value, TJSONPair(LItem).JsonValue.Value]));
        end;
     end;
  finally
     LJsonObj.Free;
  end;
end;

begin
  try
    ParseJson;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.
这将返回
Product Name Men's Sneakers
  instock : false
  size : 423
  manufacturer : Adidas
  lastcheck : 20120529
Product Name Purse
  instock : true
  size : not applicable
  manufacturer : Prada
  lastcheck : 20120528
Product Name Men's Hood
  instock : false
  size : M
  manufacturer : Generic
  lastcheck : 20120529

非常感谢,这正是我需要的,RRUZ。你的代码运行得非常好。 - Bogdan Botezatu
我不理解pair是什么,value是什么。将pair转换为数组可以工作,但不能使用'as'。我的意思是,TJSONArray(pair)可以,但(pair as TJSONArray)会导致异常。 - Mehmet Fide

6

这个网站更详细地描述了TJSONValue类型。如果您的数据是一个对象,它将具有TJSONObject类型,因此请检查其API以了解如何继续。

我相信你需要做的第一件事就是遍历它的键值对(如果不知道键名,请使用GetEnumerator,否则只需使用重载的Get - 传递字符串而不是数字)。对于每个键值对,键将是一个简单的字符串(类型为TJSONString),值可以是任何TJSONValue。重复此过程,直到到达叶子节点。

例如:

products   := jPair.Get('products');
purse      := products.GetJsonValue().Get('Purse');
purseManuf := purse.GetJsonValue().Get('manufacturer');
...

或者如果你不知道这些产品是什么:

products   := jPair.Get('products');
for prodPair in products.GetEnumerator() do
begin
    prodName := prodPair.GetJsonString();
    prodObj  := prodPair.GetJsonValue();
    ...

嗨,谢谢您指导我正确的方向。您的回答补充了@RRUZ提供的答案。 - Bogdan Botezatu
失效的链接... :-( - Legionar

1

这篇博客文章展示了一种非常现代和简单的方法来转换JSON:

uses REST.JSON; // Also new System.JSON
procedure TForm1.Button1Click(Sender: TObject);

var
  Foo: TFoo;

begin
  Foo := TFoo.Create;

  try
    Foo.Foo := 'Hello World';
    Foo.Fee := 42;
    Memo1.Lines.Text := TJson.ObjectToJsonString(Foo);
  finally
    Foo.Free;
  end;

  Foo := TJson.JsonToObject<TFoo>(Memo1.Lines.Text);

  try
    Foo.Fee := 100;
    Memo1.Lines.Add(TJson.ObjectToJsonString(Foo));
  finally
    Foo.Free;
  end;
end;

但是TFoo不包含任何子对象。 - rhody
如果该对象的属性是另一个对象,则同样适用。 - NaN
我会尝试一下。我想我只需要将subjobject声明为:property foo:TFoo读取fFoo写入fFoo; - rhody

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接