【问题标题】:Json Array into ListBox/Memo in Delphi xe7在 Delphi xe7 中将 Json 数组放入 ListBox/Memo
【发布时间】:2015-02-12 09:04:03
【问题描述】:

我正在尝试捕获以下 JSON 数组:

[{"name":"Bryan","email":"Bryan@hotmail.com"}, {"name":"Louis","email":"Louis@hotmail.com"}, {"name":"Maria","email":"Maria@hotmail.com"}, {"name":"Test","email":"test@hotmail.com"}, {"name":"Anthony","email":"anthony@hotmail.com"}]

并将其放入 Delphi 中的 MemoListBox 中: 代码如下:

procedure TForm1.Button1Click(Sender: TObject);
var jv: TJSONValue;
    jo: TJSONObject;
    jp: TJSONPair;
    ja: TJSONArray;
    i: integer;
    j: integer;
begin
    RESTRequest1.Execute;

    jv:=RESTResponse1.JSONValue;


    jo:= TJSONObject.ParseJSONValue(jv.ToString) as TJSONObject;

    try
      for i := 0 to jo.Size - 1 do
      begin
        jp := jo.Get(i);
        if jp.JsonValue is TJSONArray then
        begin
            ja := jp.JsonValue as TJSONArray;
            for j := 0 to ja.Size -1 do
              Memo1.Lines.Add(ja.Get(i).ClassName + ': ' + ja.Get(j).ToString);
        end
        else
          Memo1.Lines.Add(jp.ClassName + ': '+ jp.ToString);

      end;
    finally
      jo.Free;
    end;
end;

当我点击按钮时,我收到以下错误消息:

类类型转换无效

在调试期间,以下行有问题: jo:= TJSONObject.ParseJSONValue(jv.ToString) as TJSONObject;

我不知道如何解决这个问题或这个错误, 你能帮帮我吗?

谢谢。

【问题讨论】:

  • 显然TJSONObject.ParseJSONValue(jv.ToString)返回的值不是从TJSONObject派生的。也许是nil。你可以很容易地调试它。如果您希望我们提供帮助,您需要制作 MCVE。这样做很容易。我们没有 REST。我们需要的是jv.ToString 的值。你可以为此制作一个 10 行的 MCVE。学习如何调试确实是您的目标。
  • 好吧,如果你改变你的 JSON,它就会起作用。 {"Persons": <The array as standing above>} 对我来说很好。
  • 感谢大卫的及时答复。我想你在谈论这个例子,但你所说的 10 行 MCVE 是什么意思? jv.ToString 的值已经显示为 Json 数组
  • 你觉得json数组需要改吗?
  • 那么你为什么要把我们与所有的 REST 混淆?据我们所知,REST 不起作用。

标签: json delphi delphi-xe7


【解决方案1】:

这完全可以通过阅读代码和查看 JSON 来解决。但是,我想向您展示如何调试此类问题,以防您无法通过静态分析解决。当as 转换失败时,总是因为as 左侧的对象不是从右侧的类型派生的。然后下一步总是询问左侧对象的类型。我在上面包含了一个简短的 MCVE 作为演示的一种方式。

这个程序的输出:

{$APPTYPE CONSOLE}

uses
  System.JSON;

const
  JSON = '[{"name":"Bryan","email":"Bryan@hotmail.com"},' +
         ' {"name":"Louis","email":"Louis@hotmail.com"},' +
         ' {"name":"Maria","email":"Maria@hotmail.com"},' +
         ' {"name":"Test","email":"test@hotmail.com"},' +
         ' {"name":"Anthony","email":"anthony@hotmail.com"}]';

begin
  Writeln(TJSONObject.ParseJSONValue(JSON).ClassName);
end.

TJSONArray

现在,TJSONArray 不派生自 TJSONObject。因此,您的 as 演员表会引发运行时错误。如果您将ParseJSONValue 返回的值转换为TJSONArray 将成功。

这是意料之中的,因为您的 JSON 的根是一个数组而不是一个对象。

您需要修改您的代码,使其不假定根级别始终是一个对象。数组和对象需要不同的行为。

【讨论】:

  • 图书馆的设计是部分原因。期望名为TJSONObject.ParseJSONValue 的方法返回TJSONObject 的实例是完全可以理解的。我不明白为什么该方法不属于TJSONValue。术语也可能令人困惑。所有这些类都产生在 Delphi 术语中我们称为“JSON 对象”的实例,但只有其中一些是 JSON 称为对象的实例。新手还不清楚TJSONObject 指的是哪个(虽然也不难弄清楚)。
  • @Rob 是的,把它放在 TJSONObject 中很奇怪。虽然我确实认为超级对象更好,但我认为没有任何需要改变来解决这个问题。提问者最好退后一步并理解 JSON 术语。
【解决方案2】:

我不确定TJSONObject 的问题与您发布的字符串有关。
出于某种原因,如果您更改它,它会解析它。

{"Persons":[{"name":"Bryan","email":"Bryan@hotmail.com"},{"name":"Louis","email":"Louis@hotmail.com"},{"name":"Maria","email":"Maria@hotmail.com"},{"name":"Test","email":"test@hotmail.com"},{"name":"Anthony","email":"anthony@hotmail.com"}]}

如果我按原样运行代码,我会得到以下结果

如果您不介意使用不同于默认 Delphi 单位的东西,我建议使用超级对象 (Link here)
superobject 将解析您编辑并发布的 JSON。

您的代码如下所示:

Const
  MyJSON = '[{"name":"Bryan","email":"Bryan@hotmail.com"},{"name":"Louis","email":"Louis@hotmail.com"},{"name":"Maria","email":"Maria@hotmail.com"},{"name":"Test","email":"test@hotmail.com"},{"name":"Anthony","email":"anthony@hotmail.com"}]';    
procedure ParseJSON;
  var
    obj: ISuperObject;
    Ar: TSuperArray;
    I: integer;
  begin
    obj := SO(MyJSON);
    if obj.IsType(stArray) then
    begin
      Ar := obj.AsArray;
      try
        for I := 0 to Ar.Length-1 do
          L.Add(Ar.O[I].AsString);
      finally
        Ar.Free;
      end;
    end
    else
      L.Add(Obj.AsString);
  end;

结果:

对于 Koul,获取元素名称和值。
就像我说的不是很漂亮的代码,但没关系。

Ar.O[0].AsObject.GetNames.AsArray.S[0]

把它切成小块。

Ar.O[0] //Get the first element in the array as ISuperObject  
.AsObject //Get it as TSuperTableString
.GetNames //Gets all names in the array, in this case "name" and "email"
.AsArray[0]//Get the first name in the names array.

这将导致email(名称按 A-Z 排序)

您可以通过调用 GetValues 而不是 GetNames 对值执行相同的操作。
我认为获得它的最漂亮方法是定义 2 倍以上 TSuperArray

procedure PrintNamesAndValues;
Var
  Ar, ArNames, ArValues:TSuperArray;
  I: Integer;
begin
  Ar := SO(<JSON string>).asArray;  
  ArNames := Ar.O[0].AsObject.GetNames.AsArray;
  ArValues := Ar.O[0].AsObject.GetValues.AsArray;
  For I := 0 to ArNames.Length-1 do
    WriteLn(Format('%s: %s',[ArNames.S[I], ArValues.S[I]]));  
end;

希望一切都足够清楚:)

【讨论】:

  • 这可以使用内置的 JSON 解析器轻松解析。关键是 JSON 的根是一个数组而不是一个对象。
  • @DavidHeffernan 是的,我在发布答案时注意到了这一点。在我开始写我的时候没有看到你的答案。我稍微使用了内置的 JSON 解析器,但我更喜欢超级对象。当然这是个人喜好,使用起来感觉更轻松,更好:)
  • 问题是,提问者使用不同的解析器会遇到同样的问题。切换解析器是一个重大变化。第 1 步是了解 JSON 问题。使用的解析器对手头的问题并不重要,在我看来,它混淆了主要问题。
  • 也许吧。也许这是 ISuperObject 中的一个缺陷,但是当我解析发布的原始 JSON 时,我没有收到错误。
  • 我们可以用ISuperObject获取JSON数组每个元素的JSON字符串和JSON值吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多