I have these JSON Object , I read it well , But when i read oauth value i got an empty result ,
How I can read it ?
{
"status": true,
"message": "Success",
"items": {
"id": 6,
"name": "Tesst1",
"email": "Test1#tesst.Test",
"image": "https://www.website.com/images/man.png",
"created_at": "2017-08-24 09:18:15",
"updated_at": "2017-08-24 09:18:15",
"oauth": {
"token_type": "abc",
"expires_in": 1296000,
"access_token": "jI5NiwiZXhGVzIjpbXX0.RaC3ixxUeyYnCB6vNlwKsdjf09UeOwUJlcKmKErmE_LTAeQ-4fm8iBOKqUgpikTkyB3ztDGf4DAsaeEjUMqH76jZdbPHnX0vr676dCXkEunWoDEB8sYiHz7XRVgQ5W0O9yybA93mPO_XyrWPibkGW7GLQOApRD605N0e6vw0v9Kb_WQBim7zjTNqoLM1fSjgKJezFqf9_s3KIqBc4bjsayYLl7duzo2fzRmWtnGFfbsgO6YcaIz8ezNtWbtixLRMKnJEj1-MluqjWubsbq_gTI6yiyyac3_oY22Ge0QDdCtljadgO7wRz5VT5aJkxmJRB90u0ovm0tpGzYOO_4giY-J5egpOptjt2VZbPeM-vWPEo4-c6NYMZx6WqxBHjkZwiKUsM-tufzl5P5lFRJ9V_rBUBHEiSonTAk9FVDAwjqLc6N_IC1lsFDEC_3NBy4",
"refresh_token": "def502003e7826477c1072497ad66d7f11ea29e81a0fafef6223a63b6a0a3d18de71165afb9a340d10facca3ac7ee955aac5786a5c66a39cdf77e3f6449458e07271cbcde699aabf4d7f72dad10d586c37497216552f88460e50e9ea4944214984d5b23bac04b5f8265d132"
}
}
}
If i read the oauth as a JSON Object I got an exception about unsupported conversion,
conversion from TJSONObject to string is not supported
what is the wrong with my code ??
if JsonRespnse <> '' then
begin
jsonObiekt := TJSONObject.ParseJSONValue
(TEncoding.ASCII.GetBytes(JsonRespnse), 0) as TJSONObject;
try
try
LoginValue := jsonObiekt.get('status').JsonValue;
LoginValueIsTrue := StrToBool(LoginValue.value);
if LoginValueIsTrue = True then
begin
ItemsValue := jsonObiekt.get('items').JsonValue;
if ItemsValue is TJSONObject then
begin
strIdValue := ItemsValue.GetValue<string>('id');
// strOuthValue := ItemsValue.GetValue<string>('oauth');
OauthValue := TJSONObject(ItemsValue).get('oauth').JsonValue;
if OauthValue is TJSONObject then
Memo1.Lines.Add('oauth : ' + OauthValue.value);
end;
end;
except
on E: Exception do
ShowMessage('Read JSON : ' + E.Message);
end;
finally
jsonObiekt.Free;
end;
end;
strOuthValue := ItemsValue.GetValue<string>('oauth');
Is not working because it’s not a string in the context of the TJSON* classes. The key oauth is an json object just like 'items'.
You can read the values of the oauth object like you did it with 'items.id'
if OauthValue is TJSONObject then
begin
Memo1.Lines.Add('oauth : ' + TJSONObject(OauthValue).ToString);
Memo1.Lines.Add('oauth.token_type: ' + TJSONObject(OauthValue).Values['token_type'].ToString);
end;
Does this cover your question?
Related
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": "theemail#outlook.com",
"metadata": {
"member_name": "BLOGGS jim",
"membership_id": "4088"
}
},
{
"id": "CU000DVEKR579S",
"email": "anotheremail#outlook.com",
"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
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'];
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!
I have a JSON file which I need to parse and extract one value.
{
"user": {
"pk": 25025320,
"username": "instagram",
"full_name": "Instagram",
"is_private": false,
"profile_pic_url": "https://instagram.fmad3-5.fna.fbcdn.net/vp/295f9e76d3c26fdf613d7856e7b43348/5B4F495B/t51.2885-19/s150x150/14719833_310540259320655_1605122788543168512_a.jpg",
"profile_pic_id": "1360316971354486387_25025320",
"is_verified": true,
"has_anonymous_profile_picture": false,
"media_count": 5164,
"follower_count": 233901016,
"following_count": 187,
"biography": "Discovering — and telling — stories from around the world.",
"external_url": "",
"usertags_count": 144,
"hd_profile_pic_versions": [
{
"width": 320,
"height": 320,
"url": "https://instagram.fmad3-5.fna.fbcdn.net/vp/893534d61bdc5ea6911593d3ee0a1922/5B6363AB/t51.2885-19/s320x320/14719833_310540259320655_1605122788543168512_a.jpg"
}
],
"hd_profile_pic_url_info": {
"url": "https://instagram.fmad3-5.fna.fbcdn.net/vp/8ae8a89c80ff4722eeab592b685276cb/5B5D40A1/t51.2885-19/14719833_310540259320655_1605122788543168512_a.jpg",
"width": 320,
"height": 320
},
"has_highlight_reels": true,
"auto_expand_chaining": false
},
"status": "ok"
}
I want to extract and send the value of hd_profile_pic_url_info.url to Memo2 using the lkJSON library, Instagram API and code below to achievie this result.
var
sJSON: string;
js, Items, Item: TlkJSONbase;
I, J: Integer;
begin
sJSON := Memo1.text;
js := TlkJSON.ParseText(sJSON);
for I := 0 to Pred(js.Count) do
begin
Items := js.Child[I].Field['user'];
for J := 0 to Pred(Items.Count) do
begin
Item := Items.Child[J];
Memo2.Lines.Add(VarToStr(Item.Field['hd_profile_pic_url_info'].Value));
end;
end;
end;
You forgot that hd_profile_pic_url_info is another object containing url, width, and height, so you need to go down one more time. Here is an example of how this should be done properly.
var
js : TlkJSONbase;
begin
js := TlkJSON.ParseText(Memo1.Text);
if Assigned(js) then
begin
js := js.Field['user'];
if Assigned(js) then
begin
js := js.Field['hd_profile_pic_url_info'];
if Assigned(js) then
begin
js := js.Field['url'];
if Assigned(js) then
Memo2.Lines.Add(VarToStr(js.Value));
end;
end;
end;
end;
i use this function :
function ChildValueStr(AChild: TlkJSONbase; const Name: string; DefaultValue:
AnsiString = ''): {$IF CompilerVersion>=26}WideString{$ELSE}AnsiString{$IFEND};
var
tmpIndex: integer;
tmpStr: {$IF CompilerVersion>=26}WideString{$ELSE}AnsiString{$IFEND};
begin
Result:= DefaultValue;
if IsNil(AChild) then exit;
tmpStr:= (AChild as TlkJSONobject).{$IF CompilerVersion>=26}getWideString{$ELSE}getString{$IFEND}(Name);
if tmpStr = '' then
begin
tmpIndex := (AChild as TlkJSONobject).IndexOfName(Name);
if tmpIndex <> -1 then
Result := (AChild as TlkJSONobject).getString(tmpIndex);
end
else Result:= tmpStr;
end;
so with your code, you can use it :
var sJSON: String;
js, Items, Item: TlkJSONBase;
I, J: Integer;
begin
sJSON := Memo1.text;
js := TlkJSON.ParseText(sJSON);
for I := 0 to Pred(js.Count) do
begin
Items := js.Child[I].Field['user'];
Memo2.Lines.Add(ChildValueStr(Items, 'hd_profile_pic_url_info'));
end;
end;
I hope this can help u
Another alternative to parse JSON without use external components is the free online service https://jsontodelphi.com/
You paste the JSON text and it generate a unit with delphi classes to parse the text without problems. Better if the JSON text is complicated.
In this case, the code to access to the value is simpliest:
procedure TForm2.Button1Click(Sender: TObject);
var
root:TRootClass;
begin
root := TRootClass.FromJsonString(Memo1.Lines.Text {your JSON text});
// The caption get the URL of the pic
Caption := root.user.hd_profile_pic_url_info.url;
end;
I'm using Delphi Tokyo 10.2 Update 1 and the RESTRequest, RESTResponse and RESTClient components to communicate with a REST server. This is my first attempt with REST/JSON.
I have successfully sent a login request (POST) and received the response expected (a GUID). I then use the GUID to do various other requests (GET). Two of the requests that are made send back an empty file and document JSON template which I then have to populate. This is where I'm stuck. I'm not sure of the best way to update property values in the JSON object.
Here is the empty JSON file template I'm getting back:
{
"boxId": 0,
"changedBy": 0,
"customSort": "",
"dateChanged": "1990-01-01T00:00:00",
"dateStarted": "1990-01-01T00:00:00",
"destruction": "1990-01-01T00:00:00",
"documentCount": 0,
"documents": {
"TotalCount": 0,
"Collection": [
]
},
"extraData": {
"TotalCount": 0,
"Collection": [
]
},
"fieldDefs": {
"TotalCount": 0,
"Collection": null
},
"field": [
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
""
],
"fileId": 0,
"filePtr": 0,
"id": 0,
"isIndexed": false,
"keyValue": "",
"keyVisualValue": "",
"labelPrinted": "1990-01-01T00:00:00",
"lineItems": {
"TotalCount": 0,
"Collection": [
]
},
"notes": "",
"objectType": 5,
"projectId": 0,
"routeInfo": null,
"routingDoc": null,
"remoteId": 0,
"saveNotesOnly": false,
"saveStyle": -999,
"status": 1,
"syncFlag": 0,
"totalDocumentCount": 0,
"viewerContext": 0
}
In Python to populate the first two values in the field property array I simply would do:
inc_filetemplate = json.loads(requests.get(NEWFILE_string).text)
inc_doctemplate = json.loads(requests.get(NEWDOC_string).text)
filetemplate = inc_filetemplate
doctemplate = inc_doctemplate
filetemplate['field'][1] = dcn
filetemplate['field'][2] = batchname
EASY!!!! ;)
What is the best way to do this with Delphi?
I can get the values from the "field" array (first two items in this example which happen to be empty). Just not sure the best way to set the values for those items.
This is what I have started:
procedure PopulateFileTemplate(const AFileTemplate: String);
var
JO: TJSONObject;
JOPair: TJSONPair;
JOArray: TJSONArray;
FieldDCN: String;
FieldBatchName: String;
begin
JO := TJSONObject.ParseJSONValue(AFileTemplate) as TJSONObject;
try
if JO = nil then
begin
MessageDlg('Unable to parse JSON file template.', mtError, [mbOK], 0);
Exit;
end;
JOArray := JO.Get('field').JsonValue as TJSONArray;
FieldDCN := JOArray.Items[0].Value;
FieldBatchName := JOArray.Items[1].Value;
Memo1.Lines.Add('The old value of DCN is: ' + FieldDCN);
Memo1.Lines.Add('The old value of BatchName is: ' + FieldBatchName);
// Best way to set Values here???????
Memo1.Lines.Add('The new value of DCN is: ' + FieldDCN);
Memo1.Lines.Add('The new value of BatchName is: ' + FieldBatchName);
finally
JO.Free;
end;
end;
The TJSONArray.Items[] property returns a TJSONValue, which in your example are TJSONString objects since fields an array of strings.
TJSONArray is actually not designed to allow modifying existing objects in the array. You can add new objects to the end of the array, and remove arbitrary objects from the array, but you cannot insert new objects at arbitrary indexes, or replace existing objects with new objects.
The closest you can get to that kind of functionality is to construct a TList<TJSONValue> with the desired objects in it, and then pass it to TJSONArray.SetElements(). Not ideal.
And TJSONString does not have any methods or properties for editing its string value (except for TJSONString.AddChar()).
You could try using an accessor/helper class to gain access to the protected TJSONString.FStrBuffer member and then edit its content as needed:
type
TJSONStringAccess = class(TJSONString)
end;
procedure SetJSONStringValue(JSONValue: TJSONString; const S: string);
begin
with TJSONStringAccess(JSONValue) do
begin
FStrBuffer.Clear;
FStrBuffer.Append(S);
end;
end;
procedure PopulateFileTemplate(const AFileTemplate: String);
var
JO: TJSONObject;
JOPair: TJSONPair;
JOArray: TJSONArray;
FieldDCN: TJSONString;
FieldBatchName: TJSONString;
begin
JO := TJSONObject.ParseJSONValue(AFileTemplate) as TJSONObject;
if JO = nil then
begin
MessageDlg('Unable to parse JSON file template.', mtError, [mbOK], 0);
Exit;
end;
try
JOArray := JO.Get('field').JsonValue as TJSONArray;
FieldDCN := JOArray.Items[0] as TJSONString;
FieldBatchName := JOArray.Items[1] as TJSONString;
Memo1.Lines.Add('The old value of DCN is: ' + FieldDCN.Value);
Memo1.Lines.Add('The old value of BatchName is: ' + FieldBatchName.Value);
SetJSONStringValue(FieldDCN, '...');
SetJSONStringValue(FieldBatchName, '...');
Memo1.Lines.Add('The new value of DCN is: ' + FieldDCN.Value);
Memo1.Lines.Add('The new value of BatchName is: ' + FieldBatchName.Value);
finally
JO.Free;
end;
end;
type
TJSONStringHelper = helper class for TJSONString
procedure SetValue(const S: string);
end;
procedure TJSONStringHelper.SetValue(const S: string);
begin
Self.FStrBuffer.Clear;
Self.FStrBuffer.Append(S);
end;
procedure PopulateFileTemplate(const AFileTemplate: String);
var
JO: TJSONObject;
JOPair: TJSONPair;
JOArray: TJSONArray;
FieldDCN: TJSONString;
FieldBatchName: TJSONString;
begin
JO := TJSONObject.ParseJSONValue(AFileTemplate) as TJSONObject;
if JO = nil then
begin
MessageDlg('Unable to parse JSON file template.', mtError, [mbOK], 0);
Exit;
end;
try
JOArray := JO.Get('field').JsonValue as TJSONArray;
FieldDCN := JOArray.Items[0] as TJSONString;
FieldBatchName := JOArray.Items[1] as TJSONString;
Memo1.Lines.Add('The old value of DCN is: ' + FieldDCN.Value);
Memo1.Lines.Add('The old value of BatchName is: ' + FieldBatchName.Value);
FieldDCN.SetValue('...');
FieldBatchName.SetValue('...');
Memo1.Lines.Add('The new value of DCN is: ' + FieldDCN.Value);
Memo1.Lines.Add('The new value of BatchName is: ' + FieldBatchName.Value);
finally
JO.Free;
end;
end;
Otherwise, consider switching to a 3rd party JSON library that natively supports editing values. Such as SuperObject.
Hello I get the next result in a web API in JSON format:
[
{
"$id":"47",
"CodISO":"BIH",
"ES":"Bosnia y Herzegovina",
"EN":"Bosnia and Herzegovina"
},
{
"$id":"48",
"CodISO":"BLR",
"ES":"Bielorrusia",
"EN":"Belarus"
},
{
"$id":"49",
"CodISO":"BLZ",
"ES":"Belice",
"EN":"Belize"
},
{
"$id":"50",
"CodISO":"BOL",
"ES":"Bolivia",
"EN":"Bolivia"
},
{
"$id":"51",
"CodISO":"BON",
"ES":"Bonaire",
"EN":"Bonaire"
},
{
"$id":"52",
"CodISO":"BOT",
"ES":"Botsuana",
"EN":"Botswana"
},
{
"$id":"53",
"CodISO":"BRA",
"ES":"Brasil",
"EN":"Brazil"
},
{
"$id":"54",
"CodISO":"BRB",
"ES":"Barbados",
"EN":"Barbados"
}
]
Now, I want read the value from item 'ES' where the value of item 'CodISO' = 'BOL' in Delphi SuperObject, I'm not able to find the solution, took all day trying it.
I don't know how iterate with SuperObject elements as I do it with Embarcadero TJSONValue, TJSONObject, TJSONArray. I'm a newbie with SuperObject:
var
json: ISuperObject;
Retriever: TIdHTTP;
Url: string;
AnsiStr: AnsiString;
begin
URL := Form1.RestClient1.BaseURL;
try
Retriever := TIdHTTP.Create(nil);
try
AnsiStr := Retriever.Get(Url);
json := SO(AnsiStr);
{ Here code to iterate with json elements in SuperObject.......
.
.
.
.
}
finally
Retriever.Free;
end;
except
on E: Exception do
ShowMessage(E.ClassName + ': ' + E.Message);
end;
End;
As Sir Rufo said, you need to read the SuperObject documentation.
Try something like this:
var
JsonArr, JsonObj: ISuperObject;
Retriever: TIdHTTP;
Url, JsonStr, ES: string;
I: Integer;
begin
URL := Form1.RestClient1.BaseURL;
try
Retriever := TIdHTTP.Create(nil);
try
JsonStr := Retriever.Get(Url);
finally
Retriever.Free;
end;
JsonArr := SO(JsonStr).AsArray;
for I := 0 to JsonArr.Length-1 do
begin
JsonObj := JsonArr.O[I];
if JsonObj.S['CodISO'] = 'BOL' then
begin
ES := JsonObj.S['ES'];
Break;
end;
end;
except
on E: Exception do
ShowMessage(E.ClassName + ': ' + E.Message);
end;
end;
I can't get my server app to properly receive anything.
Sender code using a design-time TIdHTTP component, with property
Request.Accept = text/html, */*
procedure TFrmTTWebserviceTester.Button1Click(Sender: TObject);
var
lJSO : ISuperObject;
lRequest: TStringStream;
lResponse: String;
begin
lJSO := SO('{"name": "Henri Gourvest", "vip": true, "telephones": ["000000000", "111111111111"], "age": 33, "size": 1.83, "adresses": [ { "adress": "blabla", "city": "Metz", "pc": 57000 }, { "adress": "blabla", "city": "Nantes", "pc": 44000 } ]}');
lRequest := TStringStream.Create(lJSO.AsString,TEncoding.UTF8); // or ASCII
// showmessage(lRequest.DataString); Correct data
IdHTTP.Request.ContentType := 'application/json';
// idHTTP.Request.Charset := 'utf-8';
lResponse := IdHTTP.Post('http://localhost:8085/ttposttest',lRequest);
// ShowMessage(lResponse.dataString);
lRequest.Free;
lJSO := nil;
end;
Receiver is a TWebAction on a TWebModule, set for MethodType mtPost (or mtAny) with handler:
procedure TWebModuleWebServices.WebModuleWebServicesTTPostTestAction(
Sender: TObject; Request: TWebRequest; Response: TWebResponse;
var Handled: Boolean);
var S: String;
begin
S := Request.Query;
Handled := true;
end; { WebModuleWebServicesTTPostTestAction }
Request.Query is empty.
All VCL apps. I have read these SO posts and many others but must be overlooking something...
TIA, Jan
The TWebRequest.Query property returns the URL query string, which you are not sending any. That is why it is blank. Your POST data is accessible from the TWebRequest.Content and TWebRequest.RawContent properties instead.