Parsing Valid JSON with TJSONObject using Embarcadero Code Example fails with exception

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() returns -1 if parsing fails. Embarcadero's example does not check for that condition.

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).

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;

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

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

Remy's code is correct after the following adjustments:

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;