【问题标题】:Delphi parse JSON array or arrayDelphi解析JSON数组或数组
【发布时间】:2018-03-14 00:37:44
【问题描述】:

这是我希望能够解析的示例 JSON:

[
  {
    "a":{
      "username":"aaa",
      "email":"aaa@gmail.com"
    }
  },
  {
    "b":{
      "username":"bbb",
      "email":"bbb@gmail.com"
    }
  }
]

我需要对getData('b', 'email') 的调用必须输出bbb@gmail.com


我真的很难理解如何使用System.JSON 单元,但我无法得到解决方案!我希望能够编写一个从上述 JSON 结构中提取特定数据的函数。到目前为止,这是我的代码。在类构造函数中我有:

var
  FIds: TJSONArray;
begin
  FIds := TJSONObject.ParseJSONValue({json string here}) as TJSONArray;
end;

然后,在必须返回数据的函数内部,我写了这个:

// 'name' can be 'a' or 'b'  | 'data' can be 'username' or 'email'
function TTest.getData(const name, data: string): string;
var
  FValue, FValueInner: TJSONValue;
begin
  for FValue in Fids do
  begin
    if (FValue is TJSONArray) then
    begin
      //Here I am sure that I have a TJSONArray (which can be 'a' or 'b' from above)
    end;
  end;
end;

根据我上面写的,我必须检查name 的值并决定是否必须访问ab 中的数据。然后,一旦我选择了正确的 JSON 数组 ab,我必须选择是否要显示 usernameemail 字段(在 data 变量中指定)。

我该怎么做?


这是我最近的尝试,但我真的不明白该怎么做:

... same code above ...

if (FValue is TJSONArray) then
begin
  //here I want to check if the current array is a or b
  if ((FValue as TJSONArray).Items[0] as TJSONValue).Value = name then
  begin
    Farr := TJSONObject.ParseJSONValue(((FValue as TJSONArray).Items[0] as TJSONValue).ToJSON) as TJSONArray;
    try
      //here I want to get the data inside username or email
      for FValueInner in Farr do
        Result := FValueInner.GetValue<string>(data);
    finally
      Farr.Free;
    end;
  end;
end;

Farr: TJSONArray;FValueInner: TJSONValue; 在哪里

【问题讨论】:

  • 不是答案,但我很久以前就放弃了使用内置库,而是改用XSuperObject。它非常易于使用且非常轻巧。
  • @JerryDodge 也许你就是答案。实际上,我在文档上读到有 3 种读取 JSON 的方法,我试图理解一些东西,但我根本不喜欢它们,而且我也发现它们很难理解。我不想放弃,但我肯定会寻找那个 XSuperObject 是否更容易!
  • @RaffaeleRossi 在我看来,问题似乎在于您对 JSON 的总体理解,而不是 System.JSON 单元。根据您所写的内容,您将“a”和“b”视为数组,但它们是对象而不是数组!数组以 [ ] 开头,但对象以 { } 开头。如果你理解这一点,你会发现图书馆一点也不难;)
  • 没有反对这个问题,但我现在又感到困惑了。 “Delphi 解析 JSON 数组或数组” 如何比特定主题提问更易于搜索?我认为没有理由关闭甚至删除它,因为它是可以理解且可以回答的问题(因此有些问题已被关闭并从此处删除)。
  • 花点时间在json.org 学习术语

标签: json delphi


【解决方案1】:

对于寻找这些答案的新读者。

这个函数怎么样,或者如果你重构 JSON 数据更简单?

function getData(JsonString: String; User: String; Field: String): String;
var
  JSonValue: TJSonValue;
  JsonArray: TJSONArray;
  ArrayElement: TJSonValue;
  FoundValue: TJSonValue;
begin
  Result :='';

  // create TJSonObject from string
  JsonValue := TJSonObject.ParseJSONValue(JsonString);

  // get the array
  JsonArray := JsonValue as TJSONArray;

  // iterate the array
  for ArrayElement in JsonArray do begin
      FoundValue := ArrayElement.FindValue(User);
      if FoundValue <> nil then begin
        Result := ArrayElement.GetValue<string>(User + '.' + Field);
        break;
      end;
  end;
end;

上面示例 JSON 代码的问题在于它使用用户名“a”“b”作为用户数据的 JSON-key {key:data}。这样,您就不能在搜索数据时使用 GetValue("a")。以不同的方式构建 JSON 数据会使搜索过程变得容易得多。稍后我会举一个例子。

处理给定 JSON 数据的一种方法是使用 FindValue,这样您就可以检查一个字段是否带有 key“a”或“b”存在。

FoundValue := ArrayElement.FindValue("b");
if FoundValue <> nil then begin
    Result := ArrayElement.GetValue<string>("b"+ '.' + "email");
    break;

关于“解析 JSON 数组”问题:数据作为 TJSonObject 加载后,您可以将数据更改为 TJSONArray 并迭代元素。

  JsonValue := TJSonObject.ParseJSONValue(JsonString);  
  JsonArray := JsonValue as TJSONArray;
  for ArrayElement in JsonArray do begin
    ...

给定 JSON 数据的工作示例代码:

unit JsonArray1;

interface

uses System.JSON;

function getData2(JsonString: String; User: String; Field: String): String;
procedure Test1();

implementation

function getData2(JsonString: String; User: String; Field: String): String;
var
  JSonValue: TJSonValue;
  JsonArray: TJSONArray;
  ArrayElement: TJSonValue;
  FoundValue: TJSonValue;
begin
  Result :='';

  // create TJSonObject from string
  JsonValue := TJSonObject.ParseJSONValue(JsonString);

  // get the array
  JsonArray := JsonValue as TJSONArray;

  // iterate the array
  for ArrayElement in JsonArray do begin
      FoundValue := ArrayElement.FindValue(User);
      if FoundValue <> nil then begin
        Result := ArrayElement.GetValue<string>(User + '.' + Field);
        break;
      end;
  end;
end;

procedure Test1();
var
  DataBase: String;
  EmailAddress : String;
  Username: String;
begin
  DataBase := '[  {"a" : {"username":"aaa","email":"aaa@gmail.com"}},' +
                 '{"b" : {"username":"bbb","email":"bbb@gmail.com"}}  ]';

  EmailAddress := getData2(DataBase, 'b', 'email');
  Username := getData2(DataBase, 'a', 'username');

end;

end.

如前所述,使用适当的键重构 JSON 数据可使查找数据的代码更加简单。因为用户数据 "a":{}, "b":{} 之间存在 1 对 1 的关系,所以很容易引入 'user' 键。此外,向数组添加“用户”键会导致所有数据都有键。

  '{"users" : [{ "user":"a", "username":"aaa","email":"aaa@gmail.com"},' +
              '{ "user":"b", "username":"bbb","email":"bbb@gmail.com"}]}';

当您迭代用户时,您现在可以将 GetValue 与新的“用户”键一起使用。

  if ArrayElement.GetValue<String>('user') = 'b' then begin
    Result := ArrayElement.GetValue<String>('email');

通过给数组一个键,你现在可以得到数组:

JsonArray := JsonValue.GetValue<TJSONArray>('users');

重组 JSON 数据的工作示例代码:

unit JsonArray2;

interface

uses System.JSON;

function getData2(JsonString: String; User: String; Field: String): String;
procedure Test2();

implementation

function getData2(JsonString: String; User: String; Field: String): String;
var
  JSonValue: TJSonValue;
  JsonArray: TJSONArray;
  ArrayElement: TJSonValue;
  FoundValue: TJSonValue;
begin
  Result :='';

  // create TJSonObject from string
  JsonValue := TJSonObject.ParseJSONValue(JsonString);

  // get the array
  JsonArray := JsonValue.GetValue<TJSONArray>('users');

  // iterate the array
  for ArrayElement in JsonArray do begin
      if ArrayElement.GetValue<String>('user') = User then begin
        Result := ArrayElement.GetValue<String>(Field);
        break;
      end;
  end;
end;

procedure Test2();
var
  DataBase: String;
  EmailAddress : String;
  Username: String;
begin
  DataBase := '{"users" : [{ "user":"a", "username":"aaa","email":"aaa@gmail.com"},' +
                          '{ "user":"b", "username":"bbb","email":"bbb@gmail.com"}]}';

  EmailAddress := getData2(DataBase, 'b', 'email');
  Username := getData2(DataBase, 'a', 'username');

end;

end.

【讨论】:

    【解决方案2】:

    您的 JSON 是一个对象数组,因此 FIds 是一个包含 TJSONObject 元素的 TJSONArray。这些对象的ab 字段本身就是对象,而不是数组。所以FValue is TJSONArray 在枚举该数组时总是为假。

    另外,(FValue as TJSONArray).Items[0] as TJSONValue).Value = name 是错误的,因为 JSON 对象包含名称/值对,但您忽略了名称,并且您试图枚举这些对,就好像它们是数组的元素一样,而实际上它们不是。如果要枚举对象的对,请使用 TJSONObject.CountTJSONObject.Pairs[] 属性。但在这种情况下,这不是必需的,因为您正在通过名称查找特定的一对。 TJSONObject 有一个 Values[] 属性用于此目的。

    TJSONObject.ParseJSONValue(((FValue as TJSONArray).Items[0] as TJSONValue).ToJSON) as TJSONArray 简直太荒谬了。没有理由将对象转换回 JSON 字符串只是为了再次解析它。已经解析过一次,不需要再次解析。

    最后,FValueInner.GetValue&lt;string&gt;(data) 是错误的,因为TJSONValue 没有GetValue() 方法,更不用说使用泛型的方法了。

    现在,话虽如此,请尝试更多类似的东西:

    // 'name' can be 'a' or 'b'  | 'data' can be 'username' or 'email'
    function TTest.getData(const name, data: string): string;
    var
      FValue, FValueInner: TJSONValue;
    begin
      Result := '';
      for FValue in Fids do
      begin
        if (FValue is TJSONObject) then
        begin
          FValueInner := TJSONObject(FValue).Values[name];
          if FValueInner <> nil then
          begin
            if (FValueInner is TJSONObject) then
            begin
              FValueInner := TJSONObject(FValueInner).Values[data]; 
              if FValueInner <> nil then
                Result := FValueInner.Value;
            end;
            Exit;
          end;
        end;
      end;
    end;
    

    【讨论】:

    • 非常感谢。我不明白什么时候必须使用 TJSONObject 和 TJSONValue。我已经不止一次地阅读过该文档,但我仍然有一些疑问。 TJSONValue 是数组内的项目,而 TJSONObject 是通用对象吗? (数组或项)
    • TJSONValue 是所有其他 JSON 值类派生自的基类,例如 TJSONObject。你可以有一个字符串数组、一个整数数组、一个对象数组等。一个 JSON 对象包含可以是数组、整数、字符串、对象等的名称/值对。因此,API 中的所有内容都是公开的作为一般的TJSONValue 指针,然后您可以对其进行类型转换以根据需要访问特定类型的数据...
    • ... 在这种情况下,您有一个 TJSONValue 数组指向 TJSONObject 实例。其中有一个 ab 字段,它是一个 TJSONPair 包含一个 TJSONValue 指向一个 TJSONObject 实例。其中有一个usernameemail 字段,它是一个TJSONPair,包含一个指向TJSONValueTJSONValue
    【解决方案3】:

    如果你使用AlcinoeTalJsonDocument,它会很简单:

    aJsonDoc := TalJsonDocU.create;
    aJsonDoc.loadFromFile(...);
    for i := 0 to aJsonDoc.node.childnodes.count-1 do begin
      myValue := aJsonDoc.node.childNodes[i].getchildNodeValueText(['b', 'email']); 
      if myValue <> '' then break;
    end;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-08
      • 2021-11-21
      • 2010-10-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多