1

I am trying to parse a JSON result from the Twitter API using Delphi XE7. I am getting an "Invalid class typecast" error, but I check the JSON with an online verifier and it is OK.

Here is the JSON result:

[
  {
    "trends":
    [
      {
        "name":"#OneDirectionIsOverParty",
        "url":"http://twitter.com/search?q=%23OneDirectionIsOverParty",
        "promoted_content":null,
        "query":"%23OneDirectionIsOverParty",
        "tweet_volume":410022
      },
      {
        "name":"#TheDarkKnight",
        "url":"http://twitter.com/search?q=%23TheDarkKnight",
        "promoted_content":null,
        "query":"%23TheDarkKnight",
        "tweet_volume":null
      },
      {
        "name":"#QuintaComOClubeSdv",
        "url":"http://twitter.com/search?q=%23QuintaComOClubeSdv",
        "promoted_content":null,
        "query":"%23QuintaComOClubeSdv",
        "tweet_volume":23756
      }
    ],
    "as_of":"2016-07-21T20:14:13Z",
    "created_at":"2016-07-21T20:08:31Z",
    "locations":
    [
      {
        "name":"Worldwide",
        "woeid":1
      }
    ]
  }
]

This is my parsing function:

procedure ParseJSON(const JSON: string);
var
 JSONObject: TJSONObject;
 MessageText: TJSONArray;
 NodeDetails: TJSONObject;
 MsgDetail: TJSONString;
 I: Integer;
 Item: TListItem;
begin
 JSONObject := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(JSON), 0) as TJSONObject;
 MessageText := JSONObject.Get('trends').JSONValue as TJSONArray;

for I := 0 to TJSONArray(MessageText).Size - 1 do
begin
  Item := Form1.ListView1.Items.Add;
  NodeDetails := MessageText.Get(I) as TJSONObject;
  MsgDetail := NodeDetails.Get('query').JSONValue as TJSONString;
  Item.Caption := MsgDetail.Value;
end;

Actually, this function works with other JSON results from the Twitter API. It is not working on this one result only.

4
  • Which line does the debugger break on? Have you stepped through the code yet? Commented Jul 21, 2016 at 20:30
  • Hey @JerryDodge "JSONObject := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(JSON), 0) as TJSONObject;" Commented Jul 21, 2016 at 20:31
  • I'm not familiar with this particular JSON library, but my guess is that you should be parsing it as an array instead of an object - since the root element is an array. In fact, your code doesn't appear to match this JSON data at all. Commented Jul 21, 2016 at 20:33
  • Also, the code is leaking the TJSONValue that ParseJSONValue() returns. You need to Free it when you are done using it. Commented Jul 22, 2016 at 2:27

2 Answers 2

7
JSONObject := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(JSON), 0) as TJSONObject;

The root of the JSON is an array, not an object. Hence the error.

You need to cast the return value of ParseJSONValue() to TJSONArray instead of TJSONObject, and then you can access the first element in the array and read its trends value. You already have code for parsing arrays, so you clearly know how to do that.

If you are not clear on the JSON terminology of object and array, please read the JSON spec.

Sign up to request clarification or add additional context in comments.

1 Comment

Since the same code is being used to parse multiple results of Twitter, where some start with an object and some start with an array, you could save the return value of ParseJSONValue() to a local TJSONValue variable and then use the is operator to check if it is really a TJSONObject or TJSONArray before casting it.
0

As David has pointed out, the issue is that your code assumes that the JSON text is an object where in this specific case it is an array.

In situations where code does not know whether a specific JSON container is an object or an array, my alternative JSON library provides a way to deal with this by providing a TJSONText class specifically for dealing with JSON where you do not necessarily know (or care) whether the JSON involved is an object or an array.

In your case the resulting code would be something like:

response := TJSONText.CreateFromUTF8(JSON);

case response.ValueType of
  jsArray  : MessageText := response.AsArray[0].AsObject['trends'].AsArray;
  jsObject : MessageText := NIL;  // or as appropriate to extract "trends" from a single object response
end;

if Assigned(MessageText) then
begin
  .. etc etc
end;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.