使用Embarcadero代码解析使用TJSONObject的有效JSON示例失败,但有异常

Bru*_*ong 10 delphi parsing json

以下是Embarcadero帮助的示例代码(http://docwiki.embarcadero.com/RADStudio/XE5/en/JSON):

您可以使用以下代码片段之一将JSON字符串表示形式转换为JSON.

使用ParseJSONValue:

procedure ConsumeJsonString;
var
  LJSONObject: TJSONObject;

begin

  LJSONObject := nil;
  try

    { convert String to JSON }
    LJSONObject := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(GJSONString), 0) as TJSONObject;

    { output the JSON to console as String }
    Writeln(LJSONObject.ToString);
  finally
    LJSONObject.Free;
  end;
Run Code Online (Sandbox Code Playgroud)

该方法失败,并在as行上抛出类无效类型!

使用Parse:

procedure ConsumeJsonBytes;
var
  LJSONObject: TJSONObject;

begin
  LJSONObject := nil;
  try
    LJSONObject := TJsonObject.Create;
    { convert String to JSON }
    LJSONObject.Parse(BytesOf(GJSONString), 0);

    { output the JSON to console as String }
    Writeln(LJSONObject.ToString);
  finally
    LJSONObject.Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

在Embarcadero示例中,输入JSON在代码中声明为字符串:

  const
  GJSONString =
    '{' +
    '    "name": {'+
    '        "A JSON Object": {' +
    '          "id": "1"' +
    '        },' +
    '        "Another JSON Object": {' +
    '          "id": "2"' +
    '        }' +
    '    },' +
    '    "totalobjects": "2"' +
    '}';
Run Code Online (Sandbox Code Playgroud)

我正在处理的JSON来自BetFair.它是有效的(通过http://jsonformatter.curiousconcept.com/http://www.freeformatter.com/json-validator.htmlhttp://jsonlint.com/验证):

[{
    "caption": "Get the number of soccer markets",
    "methodName": "SportsAPING/v1.0/listEventTypes",
    "params": {
        "filter": {
            "eventTypeIds": [
                1
            ]
        }
    }
},
{
        "caption": "Get the next horse race in the UK",
        "methodName": "SportsAPING/v1.0/listMarketCatalogue",
        "params": {
            "filter": {
                "eventTypeIds": [
                    7
                ],
                "marketCountries": [
                    "GB"
                ],
                "marketTypeCodes": [
                    "WIN"
                ],
                "marketStartTime": {
                    "from": "2013-04-11T11:03:36Z"
                }
            },
            "sort": "FIRST_TO_START",
            "maxResults": "1",
            "marketProjection": [
                "COMPETITION",
                "EVENT",
                "EVENT_TYPE",
                "MARKET_DESCRIPTION",
                "RUNNER_DESCRIPTION"
            ]
        }
},
{
        "caption": "Get the 2 best prices, rolled up to £10 for the London Mayor Election 2016",
        "methodName": "SportsAPING/v1.0/listMarketBook",
        "params": {
            "marketIds": [
                "1.107728324"
            ],
            "priceProjection": {
                "priceData": [
                    "EX_BEST_OFFERS"
                ],
                "exBestOffersOverrides": {
                    "bestPricesDepth": "2",
                    "rollupModel": "STAKE",
                    "rollupLimit": "10"
                }
            }
        }
},
{
        "caption": "Get my current unmatched bets",
        "methodName": "SportsAPING/v1.0/listCurrentOrders",
        "params": {
            "orderProjection": "EXECUTABLE"
        }
},
{
        "caption": "Get my application keys",
        "methodName": "AccountAPING/v1.0/getDeveloperAppKeys",
        "params": {
        }
}]
Run Code Online (Sandbox Code Playgroud)

我没有将此声明为字符串,而是从文件中读取它:

TFile.ReadAllText(aFileName);
Run Code Online (Sandbox Code Playgroud)

文件读取成功.

这是导致问题的代码.我按照Embarcadero docs的建议使用了方法2,如上所述.那失败了.为了调试目的,我将方法拆分为更多变量.

根据Embarcadero文档,如果解析因任何原因失败,则vParseResult将为负值.它不是.但是,即使解析成功(尝试后的第二行)导致异常,vJSONPair也会结束为零:

procedure TfMain.loadScenarioData(aFilename: string);
var
  vJSONString: string;
  vJSONScenario: TJSONObject;
  vJSONPair: TJSONPair;
  vJSONScenarioEntry: TJSONValue;
  vJSONScenarioValue: string;
  I: Int16;
  vParseResult: Integer;
begin
  vJSONString := TFile.ReadAllText(aFileName);

  vJSONScenario := nil;

  try
  vJSONScenario := TJSONObject.Create;
  vParseResult := vJSONScenario.Parse(BytesOf(vJSONString),0);
  if  vParseResult >= 0 then
  begin
      //BetFair Specific 'caption' key
      vJSONPair := vJSONScenario.Get('caption');
      vJSONScenarioEntry := vJSONPair.JsonValue;
      vJSONScenarioValue := vJSONScenarioEntry.Value;
      cbScenario.Items.Add(vJSONScenarioValue);
  end;

  finally
      vJSONScenario.Free;

  end;
end;
Run Code Online (Sandbox Code Playgroud)

这种事情那里没有IDE和语言或者文档是不完整的或足够的适当的文件 - 是时间的可怕的浪费,并给了我完成工作中的问题.我需要使用语言和库来解决问题,而不是用不充分,模糊和难以找到的文档解决问题或更多问题.

Rem*_*eau 22

TJSONObject.ParseJSONValue()nil如果解析失败,则返回指针.Embarcadero的例子没有检查那个条件.如果解析失败,那将解释as运营商引发的"无效类型转换"错误.

TJSONObject.Parse()如果解析失败,则返回-1.Embarcadero的例子没有检查那个条件.

因为TJSONObject解析字节而不是字符,我建议你不要使用TFile.ReadAllText(),TEncoding.Default如果文件没有BOM ,它将读取字节并将它们解码为UTF-16 .在您的特定示例中,这不是问题,因为您的JSON仅包含ASCII字符.但如果使用非ASCII Unicode字符,则可能会出现问题.JSON默认使用UTF-8(这就是默认情况下IsUTF8参数TJSONObject.ParseJSONValue()为true的原因).

在任何情况下,您的代码都与您显示的JSON数据的结构不匹配.您的JSON数据是一个对象数组,因此解析的第一个项目将是a TJSONArray,而不是a TJSONObject.如果你使用TSJONObject.ParseJSONValue(),它将返回一个TJSONValue可以类型转换为TJSONArray:

procedure TfMain.loadScenarioData(aFilename: string);
var
  vJSONBytes: TBytes;
  vJSONScenario: TJSONValue;
  vJSONArray: TJSONArray;
  vJSONValue: TJSONValue;
  vJSONObject: TJSONObject;
  vJSONPair: TJSONPair;
  vJSONScenarioEntry: TJSONValue;
  vJSONScenarioValue: TJSONString;
begin
  vJSONBytes := TFile.ReadAllBytes(aFileName);

  vJSONScenario := TJSONObject.ParseJSONValue(vJSONBytes, 0);
  if vJSONScenario <> nil then
  try
    //BetFair Specific 'caption' key
    vJSONArray := vJSONScenario as TJSONArray;
    for vJSONValue in vJSONArray do
    begin
      vJSONObject := vJSONValue as TJSONObject;
      vJSONPair := vJSONObject.Get('caption');
      vJSONScenarioEntry := vJSONPair.JsonValue;
      vJSONScenarioValue := vJSONScenarioEntry as TJSONString;
      cbScenario.Items.Add(vJSONScenarioValue.Value);
    end;
  finally
    vJSONScenario.Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

或者干脆:

procedure TfMain.loadScenarioData(aFilename: string);
var
  vJSONScenario: TJSONValue;
  vJSONValue: TJSONValue;
begin
  vJSONScenario := TJSONObject.ParseJSONValue(TFile.ReadAllBytes(aFileName), 0);
  if vJSONScenario <> nil then
  try
    //BetFair Specific 'caption' key
    for vJSONValue in vJSONScenario as TJSONArray do
    begin
      cbScenario.Items.Add(((vJSONValue as TJSONObject).Get('caption').JsonValue as TJSONString).Value);
    end;
  finally
    vJSONScenario.Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

如果你TJSONObject.Parse()改为使用,那么TJSONArray它将被添加为你正在调用的对象的子节点Parse(),但它是一个未命名的数组,所以你必须通过索引检索数组:

procedure TfMain.loadScenarioData(aFilename: string);
var
  vJSONBytes: TBytes;
  vJSONScenario: TJSONObject;
  vJSONArray: TJSONArray;
  vJSONValue: TJSONValue;
  vJSONObject: TJSONObject;
  vJSONPair: TJSONPair;
  vJSONScenarioEntry: TJSONString;
  vJSONScenarioValue: string;
  vParseResult: Integer;
begin
  vJSONBytes := TFile.ReadAllBytes(aFileName);

  vJSONScenario := TJSONObject.Create;
  try
    vParseResult := vJSONScenario.Parse(vJSONBytes, 0);
    if vParseResult >= 0 then
    begin
      //BetFair Specific 'caption' key
      vJSONArray := vJSONScenario.Get(0) as TJSONArray;
      for vJSONValue in vJSONArray do
      begin
        vJSONObject := vJSONValue as TJSONObject;
        vJSONPair := vJSONObject.Get('caption');
        vJSONScenarioEntry := vJSONPair.JsonString;
        vJSONScenarioValue := vJSONScenarioEntry.Value;
        cbScenario.Items.Add(vJSONScenarioValue);
      end;
    end;
  finally
    vJSONScenario.Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

或者干脆:

procedure TfMain.loadScenarioData(aFilename: string);
var
  vJSONScenario: TJSONObject;
  vJSONValue: TJSONValue;
  vParseResult: Integer;
begin
  vJSONScenario := TJSONObject.Create;
  try
    vParseResult := vJSONScenario.Parse(TFile.ReadAllBytes(aFileName), 0);
    if vParseResult >= 0 then
    begin
      //BetFair Specific 'caption' key
      for vJSONValue in vJSONScenario.Get(0) as TJSONArray do
      begin
        cbScenario.Items.Add(((vJSONValue as TJSONObject).Get('caption').JsonValue as TJSONString).Value);
      end;
    end;
  finally
    vJSONScenario.Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

更新:如果您尝试使用SuperObject,代码会更简单一些,例如:

procedure TfMain.loadScenarioData(aFilename: string);
var
  vJSONScenario: ISuperObject;
  vJSONArray: ISuperObject;
  vJSONObject: ISuperObject;
  vJSONScenarioValue: string;
  I: Integer;
begin
  vJSONScenario := TSuperObject.ParseFile(aFileName);

  //BetFair Specific 'caption' key
  vJSONArray := vJSONScenario.AsArray;
  for I := 0 to vJSONArray.Length-1 do
  begin
    vJSONObject := vJSONArray[I].AsObject;
    vJSONScenarioValue := vJSONObject.S['caption'];
    cbScenario.Items.Add(vJSONScenarioValue);
  end;
end;
Run Code Online (Sandbox Code Playgroud)