【问题标题】:Crossplatform JSON Parsing跨平台 JSON 解析
【发布时间】:2013-02-03 23:31:40
【问题描述】:

大家晚上好。

我目前正在开发我的产品WinFlare 的跨平台兼容版本。我面临的问题是SuperObject 仍然不与 Firemonkey 跨平台兼容。无论如何,我在产品的原始版本中使用了它,但现在我想创建一个跨平台版本,而不是仅限于 Windows 的版本,我发现它很麻烦。

DBXJSON 是我经过大量研究后能够找到的唯一跨平台解决方案,但事实证明,尝试和处理这令人沮丧。我为它找到的大多数示例要么不适用于我的情况,要么它们太复杂而无法从中获得任何有用的信息。有很多讨论,但我只是在努力掌握 SuperObject 如此简单的任务。今晚的大部分时间我都在努力寻找可以构建的东西,但我所尝试的一切都让我回到了原点。

理想情况下,我想修复 SuperObject,但我缺乏深入了解使其与 OS X 跨平台兼容(并为移动工作室做好准备)的知识。我欢迎对此提出任何建议,但我想没有人有时间完成如此艰巨的任务,看来 DBXJSON 是我唯一的选择。

我处理的 JSON 布局还是一样的;

{
  response: {
    ips: [
       {
         ip: "xxx.xxx.xxx.xxx",
         classification: "threat",
         hits: xx,
         latitude: xx,
         longitude: xx,
         zone_name: "domain-example1"
         },
        {
         ip: "yyy.yyy.yyy.yyy",
         classification: "robot",
         hits: yy,
         latitude: xx,
         longitude: xx,
         zone_name: "domain-example2"
         }
       ]
   }
  result : "success",
  msg: null
}

ips 数组中可能有数百个结果。假设我想解析数组中的所有项目并提取每个 latitude 值。让我们再假设一下,我打算将它们输出到一个数组中。这是我想使用的那种代码模板;

procedure ParseJsonArray_Latitude(SInput : String);
var
  i : Integer;
  JsonArray : TJsonArray;
Begin
  // SInput is the retrieved JSON in string format
  { Extract Objects from array }

  for i := 0 to JsonArray.Size-1 do
  begin
    Array_Latitude[i] := JsonArray.Item[i].ToString;
  end;
end;

基本上,在上面写着{ Extract Objects from array } 的地方,我想要使用DBXJSON最基本 解决方案来解决我的问题。显然,我在上面的模板中显示的与JsonArray 相关的调用可能不正确——它们只是为了提供帮助。

【问题讨论】:

  • SuperObject 只是文本处理。我看不出它如何不适用于所有平台
  • SuperObject 使用WindowsWinSock 单位,以及大量使用Windows 专用例程的过程。它确实有一些适用于 FPC 和 UNIX 的 IFDEFS,但没有适用于 MACOS。它深深嵌入在代码中。
  • 很公平。不过看起来很穷。
  • 看看code.google.com/p/dwscript/source/browse JSON 单元可以“单独”使用。
  • 我确实研究了 dwscript,但与 SuperObject 一样,dwsJSON.pas 也引入了一些其他与 OS X 不跨平台兼容的单元(例如dwsXPlatform)。

标签: json delphi delphi-xe3 firemonkey-fm2


【解决方案1】:

首先,解析字符串得到一个对象。

var
  obj: TJsonObject;

obj := TJsonObject.ParseJsonValue(SInput) as TJsonObject;

这为您提供了一个具有三个属性的对象,即响应、结果和消息。尽管ParseJsonValueTJsonObject 的一个方法,并且您的特定字符串输入恰好代表一个对象值,但它可以返回任何TJsonValue 后代的实例,具体取决于它给出的JSON 文本。知道从哪里开始可能是使用 DbxJson 最困难的部分。

接下来,获取响应属性值。

response := obj.Get('response').JsonValue as TJsonObject;

那个结果应该是另一个对象,这次有一个属性,ips。获取该属性,它应该有一个值的数组。

ips := response.Get('ips').JsonValue as TJsonArray;

最后,您可以从数组中获取值。看起来您希望这些值是数字,因此您可以这样转换它们。

for i := 0 to Pred(ips.Size) do
  Array_Latitude[i] := (ips.Get(i) as TJsonObject).Get('latitude').JsonValue as TJsonNumber;

完成后记得释放obj,但不要释放此处提到的其他变量。

【讨论】:

  • 仍然有一些问题。 TJsonValue 没有 .get 例程(我也尝试使用 TJsonObject 但这会导致代码后面出现问题)。我仍然对responseids 是什么类型感到困惑(旁注ids 可能应该阅读ips 作为对问题中JSON 的引用)。我设法构建了一个 35 行长的解决方案 - 尽管它没有输出到动态数组(而不是直接输出到备忘录),但您的解决方案暗示它应该更短。
  • 我已经修复了 ids/ips 错误,并根据分配期间转换为的类型明确了变量的类型。由于缺乏细节,我无法解决您提到的其他问题。你在做什么你认为你不应该做的事?
  • 感谢罗布。我将您的答案标记为解决方案,因为它使我足够接近以便能够从那里弄清楚(一旦澄清了类型)。我很快就会发布我的解决方案作为答案。
【解决方案2】:

为了完成,由于问题指出对于跨平台没有替代 DBXJSON,我想指出两个开源替代方案,从最初的问题开始出现。

SynCrossPlatformJSON 能够创建 schema-less 对象或数组,通过自定义 variant 类型(包括 late-binding)将它们序列化和反序列化为 JSON访问属性。

对于你的问题,你可以写:

var doc: variant;
    ips: PJSONVariantData; // direct access to the array
    i: integer;
...
  doc := JSONVariant(SInput);   // parse JSON Input and fill doc custom variant type
  if doc.response.result='Success' then       // easy late-binding access
  begin
    ips := JSONVariantData(doc.response.ips); // late-binding access into array
    SetLength(Arr_Lat,ips.Count);
    for i := 0 to ips.Count-1 do begin
      Arr_lat[i] := ips.Values[i].latitude;
      Memo1.Lines.add(ips.Values[i].latitude); 
     end;
  end;  
... // (nothing to free, since we are using variants for storage)

后期绑定和变体存储允许代码可读性强。

【讨论】:

  • 这个问题清楚地要求使用 DBXJSON 的解决方案:“我想要最基本的解决方案 使用 DBXJSON 来解决我的问题”。我不确定如何为您的单位和使用它的来源发布广告是该问题的答案。您能否阐明这如何解决使用 DBXJSON 的问题?
  • 因为他找不到替代品。并且至少有 xsuperobject 和我们的单位。
  • 那么您只是决定忽略所提出的具体问题,即如何使用 DBXJSON 做到这一点?询问工具/库推荐的问题将是题外话,但这个问题并没有这样做 - 很清楚这里被问到的工具。 “我该如何修理我的保时捷?好吧,如果你有一辆宝马,你可以......”不是一个合适的答案。
  • 请再次阅读问题的开头以了解上下文。
  • @KenWhite 引用:I'd welcome any suggestions on that, but (...) it looks like DBXJSON is my only option. - 现在至少有一个选项没有 DBXJSON,所以应该受到欢迎
【解决方案3】:

感谢Rob Kennedy 的帮助,我设法构建了一个解决问题的解决方案;

var
  obj, response, arrayobj : TJSONObject;
  ips : TJSONArray;
  JResult : TJsonValue;
  i : Integer;
  Arr_Lat : Array of string;
begin
try
  Memo1.Lines.Clear;
  obj := TJsonObject.ParseJSONValue(SInput) as TJSONObject;
  response := Obj.Get('response').JsonValue as TJSONObject;
  ips := response.Get('ips').JsonValue as TJSONArray;
  SetLength(Arr_Lat, ips.Size-1);
  for i := 0 to ips.Size-1 do
    begin
      arrayobj := ips.Get(i) as TJSONObject;
      JResult := arrayobj.Get('latitude').JsonValue;
      Arr_lat[i] := JResult.Value;
      Memo1.Lines.Add(JResult.Value);
    end;
finally
  obj.Free;
end;

这会将结果添加到两个数组 (Arr_Lat),并将它们输出到备忘录 (Memo1)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 2011-02-06
    相关资源
    最近更新 更多