Parse valid JSON with TJSONObject using Embarcadero sample code. Error with exception

Here is a sample code from Embarcadero help ( http://docwiki.embarcadero.com/RADStudio/XE5/en/JSON ):

you can convert the string representation of JSON to JSON with one of the following code snippets.

Using 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; 

This approach fails with an invalid class type different from line to line !!

Use Analysis :

 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 JSON input is declared as a string as a string:

  const GJSONString = '{' + ' "name": {'+ ' "A JSON Object": {' + ' "id": "1"' + ' },' + ' "Another JSON Object": {' + ' "id": "2"' + ' }' + ' },' + ' "totalobjects": "2"' + '}'; 

JSON is processed from BetFair. It is valid (verified from 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 do not declare this as a string, but I read it from a file:

 TFile.ReadAllText(aFileName); 

Reading file successfully.

Here is the code that causes the problem. I used approach 2 as recommended in the Embarcadero docs as shown above. This failed. I split the approach into more variables for debugging purposes.

According to the documents Embarcadero vParseResult will be a negative value if parsing for any reason is not possible. Is not. However, vJSONPair ends with zero, even if the parsing succeeded (second line after the attempt), which throws 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; 

Things like this, where there is no 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 shutting down. I need to solve problems using the language and libraries, and not solve problems with them or more in terms of inadequate, ambiguous, and hard-to-reach documentation.

+8
source share
3 answers

TJSONObject.ParseJSONValue() returns a nil pointer if parsing is not performed. Example Embarcadero does not verify this condition. If the parsing is not completed, this will result in an "invalid type" error caused by the as statement.

TJSONObject.Parse() returns -1 if parsing is not performed. Example Embarcadero does not verify this condition.

Since TJSONObject parses bytes, not characters, I suggest you not use TFile.ReadAllText() , which will read bytes and decode them in UTF-16 using TEncoding.Default if there is no specification in the file. In your specific example, this is not a problem, since your JSON contains only ASCII characters. But this can be a problem if you use Unicode characters without ASCII characters. JSON uses UTF-8 by default (therefore, the IsUTF8 parameter TJSONObject.ParseJSONValue() is true by default).

In any case, your code does not match the JSON data structure you specified. Your JSON data is an array of objects, so the first parsed element will be a TJSONArray , not a TJSONObject . If you use TSJONObject.ParseJSONValue() , it will return a TJSONValue , which can be entered with a TJSONArray type:

 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() TJSONArray , TJSONArray will be added as a child of the object you are calling Parse() , but this is an unnamed array, so you need to get 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; 

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 SuperObject , the code will be a little simpler, for example:

 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; 
+18
source

The Remy code is correct after the following settings:

 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; 
+1
source

I am completely new in JSON, I am trying to use the latest code: "Remy code is correct after the following settings:

I also tried all the codes here, and they all do not return anything to me, in the latter case vJSONScenario <> nil is nil, so I do not do anything.

thanks

0
source

Source: https://habr.com/ru/post/972425/


All Articles