user2834566
user2834566

Reputation: 805

Correct syntax to read the value of a JSON string inside nested object using TJSONDataObject

Can someone please help me with the correct syntax to access a string inside a nested object using JSONDataObjects from Andreas Hausladen

I have a JSON string returned from an API that contains an array of customers followed by a cursor pagination object (meta) eg

  {
  "customers": [
    {
      "id": "CU000DWEQWMRN4",
      "email": "[email protected]",
      "metadata": {
                         "member_name": "BLOGGS jim",
                         "membership_id": "4088"
                       }
    },
    {
      "id": "CU000DVEKR579S",
      "email": "[email protected]",
      "metadata": {
                        "membership_id": "5647"
                         }
    }
  ],
  "meta": {
              "cursors": {
                             "before": null,
                             "after": "ID456"
                             },
                  "limit": 50
               }
}

I an trying to get the values of before and after inside the meta object at the end

What I have tried

var  Obj: TJsonObject;
       AfterID : string;
  
  begin
  Obj := TJsonObject.Parse(theJSON) as TJsonObject;
  AfterID :=    Obj['meta']['cursors']['after']  ; 

Problem I get the error message cannot convert object to string

I also tried

  var  
     Obj: TJsonObject;
     AfterID : string;
   begin   
   obj :=  Obj['meta'];
   Obj := Obj['cursors'];
   AfterID := Obj['after'];

Problem I get the error message cannot convert object to string

I've also tried many other combinations of syntax but I won't clutter the question with all of those!

I am puzzled as I my syntax is correct when doing the same thing with a slightly different JSON retuned from another API that has a simpler cursor pagination structure eg

{
  "items": [
    {
      "event": "accepted",
      "id": "G3wOhh",
      "user-variables": {},
      "log-level": "info",
      "method": "smtp"
    },
    {
      "event": "accepted",
      "id": "KLT56",
      "user-variables": {},
      "log-level": "info",
      "method": "smtp"
    }
  ],
  "paging": {
    "previous": "line 4",
    "first": "line 1",
    "last": "line 12",
    "next": "line 6"
  }
}

with this JSON, using

var
      Obj : TJsonObject;
      nextID : string;
begin
 Obj := TJsonObject.Parse(TheReturnedJSON) as TJDOJsonObject; 
 nextID := Obj['paging']['next'];

I get the correct value returned

Upvotes: 1

Views: 577

Answers (2)

user2834566
user2834566

Reputation: 805

Incidentally, I'll add to Remy's answer to say that since the value of 'before' in my sample JSON was null, I found I had to use the following code to avoid getting another typecast error when reading that value as Obj.Values['meta'].O['cursors'].S['before'];

var
  temp : variant;
  BeforeID : string;
begin
  //read the value into a variant by using .V just in case it's null. 
  //if it is then reading a string using .S gives an error
  temp := Obj.Values['meta'].O['cursors'].V['before']; 
  if not VarIsNull(temp) then  // it's not a null so we can convert to a string
    BeforeID := VarToStr(temp)
  else                         // it is null so assign whatever string is appropriate  
    BeforeID := '';  

But using .V was just a guess. I'd love to know where all this is documented!

Upvotes: 1

Remy Lebeau
Remy Lebeau

Reputation: 596582

TJsonObject's default [] property is its Values[] property, which returns a TJsonDataValueHelper:

property Values[const Name: string]: TJsonDataValueHelper read GetValue write SetValue; default;

So, reading Obj['cursors'] returns a TJsonDataValueHelper, not a TJsonObject.

TJsonDataValueHelper's default [] property is its O[] property, which returns a TJsonDataValueHelper representing a JSON object, not a JSON string:

property O[const Name: string]: TJsonDataValueHelper read {$IFDEF BCB}GetObj{$ELSE}GetObject{$ENDIF} write SetObject; default;

TJsonDataValueHelper has an S[] property to read a string value:

property S[const Name: string]: string read GetObjectString write SetObjectString;        // returns '' if property doesn't exist, auto type-cast except for array/object

So, try this instead:

AfterID := Obj['meta']['cursors'].S['after']; 
nextID := Obj['paging'].S['next']; 

Alternatively, don't use TJsonDataValueHelper at all. TJsonObject has an O[] property that returns a TJsonObject, and an S[] property for reading a string:

property S[const Name: string]: string read GetString write SetString;        // returns '' if property doesn't exist, auto type-cast except for array/object
...
property O[const Name: string]: TJsonObject read {$IFDEF BCB}GetObj{$ELSE}GetObject{$ENDIF} write SetObject;   // auto creates object on first access

For example:

AfterID := Obj.O['meta'].O['cursors'].S['after']; 
nextID := Obj.O['paging'].S['next']; 

Upvotes: 1

Related Questions