使用 Embarcadero 代码示例使用 TJSONObject 解析有效 JSON 失败并出现异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24815625/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Parsing Valid JSON with TJSONObject using Embarcadero Code Example fails with exception
提问by Bruce Long
Here is the example code from the Embarcadero help (http://docwiki.embarcadero.com/RADStudio/Rio/en/JSON):
以下是 Embarcadero 帮助中的示例代码 ( http://docwiki.embarcadero.com/RADStudio/Rio/en/JSON):
you can transform the JSON string representation into a JSON with one of the following code snippets.
您可以使用以下代码片段之一将 JSON 字符串表示形式转换为 JSON。
Using ParseJSONValue:
使用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;
That approach fails with a class invalid type cast on the as line !!
该方法失败,并在 as 行上强制转换了类无效类型!!
Using 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;
In the Embarcadero example The input JSON is declared in code as a string:
在 Embarcadero 示例中,输入 JSON 在代码中声明为字符串:
const
GJSONString =
'{' +
' "name": {'+
' "A JSON Object": {' +
' "id": "1"' +
' },' +
' "Another JSON Object": {' +
' "id": "2"' +
' }' +
' },' +
' "totalobjects": "2"' +
'}';
The JSON I am processing is from BetFair. It is valid (verified with http://jsonformatter.curiousconcept.com/and http://www.freeformatter.com/json-validator.htmland http://jsonlint.com/):
我正在处理的 JSON 来自 BetFair。它是有效的(通过http://jsonformatter.curiousconcept.com/和http://www.freeformatter.com/json-validator.html和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);
The file read is successfully.
文件读取成功。
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 文档中推荐的方法 2。那失败了。出于调试目的,我将方法拆分为更多变量。
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:
根据 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;
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.
这种没有足够的 IDE 和语言文档,或者文档不完整或不充分的事情 - 是一种可怕的时间浪费,给我完成工作带来了问题。我需要使用语言和库来解决问题,而不是用它们解决问题,或者更多地解决不充分、模棱两可且难以找到的文档。
回答by Remy Lebeau
TJSONObject.ParseJSONValue()returns a nilpointer 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 asoperator.
TJSONObject.ParseJSONValue()nil如果解析失败,则返回一个指针。Embarcadero 的示例不检查该条件。如果解析失败,这将解释as操作员引发的“无效类型转换”错误。
TJSONObject.Parse()returns -1 if parsing fails. Embarcadero's example does not check for that condition.
TJSONObject.Parse()如果解析失败,则返回 -1。Embarcadero 的示例不检查该条件。
Because TJSONObjectparses bytes, not characters, I suggest you not use TFile.ReadAllText(), which will read bytes and decode them to UTF-16 using TEncoding.Defaultif 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 IsUTF8parameter of TJSONObject.ParseJSONValue()is true by default).
因为TJSONObject解析字节,而不是字符,我建议您不要使用TFile.ReadAllText(),TEncoding.Default如果文件没有 BOM ,它将读取字节并将它们解码为 UTF-16 。在您的特定示例中,这不是问题,因为您的 JSON 仅包含 ASCII 字符。但如果使用非 ASCII Unicode 字符,这可能是一个问题。JSON 默认使用 UTF-8(这就是为什么IsUTF8参数TJSONObject.ParseJSONValue()默认为 true)。
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 TJSONValuethat can be type-casted to TJSONArray:
在任何情况下,您的代码都与您显示的 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;
Or simply:
或者干脆:
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;
If you use TJSONObject.Parse()instead, the TJSONArraywill 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:
如果您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;
Or simply:
或者干脆:
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;
Update:If you try SuperObjectinstead, the code would be a little simpler, eg:
更新:如果您尝试使用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;
回答by Bruce Long
Remy's code is correct after the following adjustments:
Remy的代码经过以下调整后是正确的:
var
vJSONBytes: TBytes;
vJSONScenario: TJSONValue;
vJSONArray: TJSONArray;
vJSONValue: TJSONValue;
vJSONObject: TJSONObject;
vJSONPair: TJSONPair;
vJSONScenarioEntry: TJSONString;
vJSONScenarioValue: TJSONValue;
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(pScenarioKey);
vJSONScenarioEntry := vJSONPair.JsonString;
//vJSONScenarioValue := vJSONScenarioEntry.Value;
vJSONScenarioValue := vJSONPair.JsonValue;
cbScenario.Items.Add(vJSONScenarioValue.ToString);
end;
finally
vJSONScenario.Free;
end;
end;

