Voici l'exemple de code de l'aide d'Embarcadero ( http://docwiki.embarcadero.com/RADStudio/XE5/en/JSON ):
vous pouvez transformer la représentation de chaîne JSON en JSON avec l'un des extraits de code suivants.
En utilisant 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;
Cette approche échoue avec un type non valide de classe casté sur la ligne as !!
Utilisation de 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;
Dans l'exemple Embarcadero Le JSON d'entrée est déclaré dans le code sous forme de chaîne:
const
GJSONString =
'{' +
' "name": {'+
' "A JSON Object": {' +
' "id": "1"' +
' },' +
' "Another JSON Object": {' +
' "id": "2"' +
' }' +
' },' +
' "totalobjects": "2"' +
'}';
Le JSON que je traite provient de BetFair. Il est valide (vérifié avec http://jsonformatter.curiousconcept.com/ et http://www.freeformatter.com/json-validator.html et 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": {
}
}]
Je ne déclare pas cela comme une chaîne, mais je le lis à partir du fichier ainsi:
TFile.ReadAllText(aFileName);
La lecture du fichier a réussi.
Voici le code qui cause le problème. J'ai utilisé l'approche 2 telle que recommandée par les documents Embarcadero comme indiqué ci-dessus. Cela a échoué. J'ai divisé l'approche en plusieurs variables à des fins de débogage.
Selon les documents Embarcadero, vParseResult sera une valeur négative si l'analyse échoue pour une raison quelconque. Ce ne est pas. Cependant, vJSONPair finit par zéro même si l'analyse réussit (deuxième ligne après l'essai), ce qui conduit à une 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;
Ce genre de chose où il n'y a pas de documentation adéquate pour le IDE et la langue ou où la documentation n'est pas complète ou adéquate - est une perte de temps terrible et me pose des problèmes pour terminer le travail. J'ai besoin résoudre des problèmes en utilisant le langage et les bibliothèques, ne pas résoudre les problèmes avec eux ou plus au point avec une documentation inadéquate, ambiguë et difficile à trouver.
TJSONObject.ParseJSONValue()
renvoie un pointeur nil
si l'analyse échoue. L'exemple d'Embarcadero ne vérifie pas cette condition. Si l'analyse échouait, cela expliquerait l'erreur "conversion de type non valide" générée par l'opérateur as
.
TJSONObject.Parse()
renvoie -1 si l'analyse échoue. L'exemple d'Embarcadero ne vérifie pas cette condition.
Parce que TJSONObject
analyse les octets, pas les caractères, je vous suggère de ne pas utiliser TFile.ReadAllText()
, qui lira les octets et les décodera en UTF-16 en utilisant TEncoding.Default
Si le fichier n'a pas avoir une nomenclature. Dans votre exemple particulier, ce n'est pas un problème car votre JSON ne contient que des caractères ASCII. Mais cela peut être un problème si des caractères Unicode non ASCII sont utilisés. JSON utilise UTF-8 par défaut ( c'est pourquoi le paramètre IsUTF8
de TJSONObject.ParseJSONValue()
est vrai par défaut).
Dans tous les cas, votre code ne correspond pas à la structure des données JSON que vous avez affichées. Vos données JSON sont un tableau d'objets, donc le premier élément analysé sera un TJSONArray
, pas un TJSONObject
. Si vous utilisez TSJONObject.ParseJSONValue()
, il renverra un TJSONValue
qui peut être transtypé en 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;
Ou simplement:
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;
Si vous utilisez TJSONObject.Parse()
à la place, le TJSONArray
sera ajouté en tant qu'enfant de l'objet que vous appelez Parse()
, mais il s'agit d'un tableau sans nom, vous devez donc récupérer le tableau par 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;
Ou simplement:
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;
Mise à jour: Si vous essayez SuperObject à la place, le code serait un peu plus simple, par exemple:
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;
Le code de Remy est correct après les ajustements suivants:
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;