使用Embarcadero代码示例使用TJSONObject解析有效JSON失败,并出现异常 [英] Parsing Valid JSON with TJSONObject using Embarcadero Code Example fails with exception
问题描述
以下是Embarcadero帮助中的示例代码( http://docwiki.embarcadero .com/RADStudio/Rio/en/JSON ):
Here is the example code from the Embarcadero help (http://docwiki.embarcadero.com/RADStudio/Rio/en/JSON):
您可以使用以下代码片段之一将JSON字符串表示形式转换为JSON.
you can transform the JSON string representation into a JSON with one of the following code snippets.
使用 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;
该方法失败,并且在as行上强制转换为类无效类型!
That approach fails with a class invalid type cast on the as line !!
使用解析:
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;
在Embarcadero示例中,输入JSON在代码中声明为字符串:
In the Embarcadero example The input JSON is declared in code as a string:
const
GJSONString =
'{' +
' "name": {'+
' "A JSON Object": {' +
' "id": "1"' +
' },' +
' "Another JSON Object": {' +
' "id": "2"' +
' }' +
' },' +
' "totalobjects": "2"' +
'}';
我正在处理的JSON来自BetFair.有效(已通过 http://jsonformatter.curiousconcept.com/和 http://jsonlint.com/):
The JSON I am processing is from BetFair. It is valid (verified with http://jsonformatter.curiousconcept.com/ and http://www.freeformatter.com/json-validator.html and http://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": {
}
}]
我不是将其声明为字符串,而是从文件中读取它:
I am not declaring this as a string, but reading it from file thus:
TFile.ReadAllText(aFileName);
文件读取成功.
这是导致问题的代码.我使用了Embarcadero文档中推荐的方法2,如上所示.那失败了.我将方法划分为更多变量以进行调试.
Here is the code that causes the problem. I have used approach 2 as recommended from Embarcadero docs as in line above. That failed. I split the approach across more variables for debugging purposes.
根据Embarcadero文档,vParseResult将为负数 如果解析由于任何原因而失败.它不是.但是,即使解析成功(尝试后的第二行),vJSONPair最终也仍然为nil,这会导致异常:
According to the Embarcadero docs vParseResult will be a negative value if parsing fails for any reason. It does not. However, vJSONPair ends up nil even though the parse succeeds (second line after the try) which leads to an exception:
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;
在没有足够的IDE和语言文档或文档不完整或不充分的情况下,这种事情很浪费时间,给我带来了完成工作的麻烦.我需要使用语言和库来解决问题,而不是用不足,模棱两可且难以找到文档的方式解决它们或更多的问题.
This kind of thing where there is not adequate documentation for the IDE and language or where the documentation is not complete or adequate - is a terrible waste of time and gives me problems with completing work. I need to be solving problems using the language and libraries, not solving problems with them or more to the point with inadequate, ambiguous, and hard to find documentation.
推荐答案
TJSONObject.ParseJSONValue()
返回nil
指针. Embarcadero的示例不检查这种情况.如果解析失败,则将说明as
运算符引发的无效类型转换"错误.
TJSONObject.ParseJSONValue()
returns a nil
pointer if parsing fails. Embarcadero's example does not check for that condition. If parsing failed, that would account for the "invalid type cast" error being raised by the as
operator.
TJSONObject.Parse()
返回-1. Embarcadero的示例不检查这种情况.
TJSONObject.Parse()
returns -1 if parsing fails. Embarcadero's example does not check for that condition.
因为TJSONObject
解析字节而不是字符,所以我建议您不要使用TFile.ReadAllText()
,如果文件没有BOM,它将读取字节并将其使用TEncoding.Default
解码为UTF-16.在您的特定示例中,这不是问题,因为您的JSON仅包含ASCII字符.但是,如果使用非ASCII Unicode字符,则可能会出现问题. JSON默认情况下使用UTF-8(这就是TJSONObject.ParseJSONValue()
的IsUTF8
参数默认为true的原因.)
Because TJSONObject
parses bytes, not characters, I suggest you not use TFile.ReadAllText()
, which will read bytes and decode them to UTF-16 using TEncoding.Default
if the file does not have have a BOM. In your particular example, that is not an issue since your JSON contains only ASCII characters. But that can be an issue if non-ASCII Unicode characters are used. JSON uses UTF-8 by default (which is why the IsUTF8
parameter of TJSONObject.ParseJSONValue()
is true by default).
无论如何,您的代码与显示的JSON数据的结构不匹配.您的JSON数据是一个对象数组,因此解析的第一项将是TJSONArray
,而不是TJSONObject
.如果使用TSJONObject.ParseJSONValue()
,它将返回一个TJSONValue
,可以将其类型转换为TJSONArray
:
In any case, your code does not match the structure of the JSON data you have shown. Your JSON data is an array of objects, so the first item parsed will be a TJSONArray
, not a TJSONObject
. If you use TSJONObject.ParseJSONValue()
, it will return a TJSONValue
that can be type-casted to 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;
或者简单地:
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;
如果改用TJSONObject.Parse()
,则会将TJSONArray
作为您正在调用Parse()
的对象的子对象添加,但是它是未命名的数组,因此您必须按索引检索该数组:>
If you use TJSONObject.Parse()
instead, the TJSONArray
will get added as a child of the object you are calling Parse()
on, but it is an unnamed array so you have to retrieve the array by index:
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;
或者简单地:
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;
更新:如果您尝试使用 SuperObject ,则代码会更简单一些,例如:
Update: If you try SuperObject instead, the code would be a little simpler, eg:
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;
这篇关于使用Embarcadero代码示例使用TJSONObject解析有效JSON失败,并出现异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!