JSON parsing "NULL" - json

I have this code working until today. If there is for egxample this:
"status":null
there will be exception "Invalid class type". How to fix it? Thanks for help.
procedure TForm1.Button10Click(Sender: TObject);
var
IdHTTP: TIdHTTP;
IdSSL: TIdSSLIOHandlerSocketOpenSSL;
JSON: string;
jsonObiekt: TJSONObject;
streams: TJSONArray;
stream: TJSONObject;
channel: TJSONObject;
status: TJSONString;
liczbaStrumieni: integer;
i: integer;
begin
IdHTTP := TIdHTTP.Create;
try
IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP);
IdHTTP.IOHandler := IdSSL;
IdHTTP.Request.Accept := 'application/vnd.twitchtv.v3+json';
IdHTTP.Request.CustomHeaders.AddValue('Client-ID', 'smb61nyd0vxmqdn9d3k735qbx41cdyg');
JSON := IdHTTP.Get('https://api.twitch.tv/kraken/streams?game=StarCraft:%20Brood%20War');
finally
IdHTTP.Free;
end;
jsonObiekt := TJSONObject.ParseJSONValue(JSON) as TJSONObject;
try
streams := jsonObiekt.Get('streams').JsonValue as TJSONArray;
liczbaStrumieni := streams.Size;
for i := 0 to liczbaStrumieni - 1 do
begin
stream := streams.Get(i) as TJSONObject;
channel := stream.Get('channel').JsonValue as TJSONObject;
status := channel.Get('status').JsonValue as TJSONString;
Memo6.Lines.Add(status.Value);
end;
finally
jsonObiekt.Free;
end;
end;

I see from the comments that you're familiar with the JSON specification on JSON.org. Your problem appears to be with understanding how it maps to the DBXJSON model.
Specifically, TJSONObject represents an Object as defined by the JSON standard. It does not mean "a (Delphi) object that holds JSON data." That's what TJSONValue is there for. Try using that instead.

Related

JSON nested object shows empty in rest debugger and RestResponse (XE6)

I use C++Builder XE6 Pro and have the following JSON response (see the full response here):
[
{
"id":"10186",
"dataset":"krs_podmioty",
"url":"https://api-v3.mojepanstwo.pl/dane/krs_podmioty/10186",
"mp_url":"https://mojepanstwo.pl/dane/krs_podmioty/10186",
"schema_url":"https://api-v3.mojepanstwo.pl/schemas/dane/krs_podmioty.json",
"global_id":"3157847",
"slug":"bank-millennium",
"score":12.13878,
"data":
{
"krs_podmioty.nazwa_organu_reprezentacji":"ZARZĄD",
"krs_podmioty.dotacje_ue_beneficjent_id":"0",
"krs_podmioty.liczba_prokurentow":0,
...
"gpw":true
}
...
]
I am using REST components, but when I try to parse this, both in the REST Debugger and at design/run-time, I am getting empty values for the "dataset":"krs_podmioty" elements, but the "gpw":true elements show correctly.
I've choosen the JSON root element as DataObject, and marked Nested
and set NestedElementDepth to 3. I have tried another settings as well, but without success.
How to get the "dataset":"krs_podmioty" values correctly ?
This is how I'm able to read those values:
uses
System.Json;
procedure TForm1.Button1Click(Sender: TObject);
var
jsonText: string;
jsonArray: TJsonArray;
dataObj: TJsonObject;
Jobj: TJsonObject;
krsPodmiotyNazwaOrganuReprezentacjiValue: TJsonValue;
krsPodmiotyDotacjeUeBeneficjentIdValue: TJsonValue;
krsPodmiotyLiczbaProkurentowValue: TJsonValue;
begin
jsonText := '[{Your JSON content here}]';
Jobj := TJSONObject.ParseJSONValue(jsonText) as TJsonObject;
jsonArray :=Jobj.GetValue('Dataobject') as TJsonArray;
dataObj := (jsonArray.Items[0] as TJsonObject).GetValue('data') as TJsonObject;
krsPodmiotyNazwaOrganuReprezentacjiValue := dataObj.GetValue('krs_podmioty.nazwa_organu_reprezentacji');
krsPodmiotyDotacjeUeBeneficjentIdValue := dataObj.GetValue('krs_podmioty.dotacje_ue_beneficjent_id');
krsPodmiotyLiczbaProkurentowValue := dataObj.GetValue('krs_podmioty.liczba_prokurentow');
MessageDlg(krsPodmiotyNazwaOrganuReprezentacjiValue.Value, mtInformation, [mbOK], 0, mbOK);
MessageDlg(krsPodmiotyDotacjeUeBeneficjentIdValue.Value, mtInformation, [mbOK], 0, mbOK);
MessageDlg(krsPodmiotyLiczbaProkurentowValue.Value, mtInformation, [mbOK], 0, mbOK);
end;
However, if you are not using anything from the System.Json namespace, this might not answer your question.

When refactoring my datasnap server cannot marshall if i moved a class to a different unit

I have a datasnap server that I have to keep running or at least cannot update.
I would like to connect to it with a new Client.
All the code is the same but I refactored some code so now a class I use to connect to the server is in another unit.
The servermethods is a function that returns TMyObject. But in reality the server returns a marshalled object of type Oldunit.TMyObject. But this is not recognized by the client.
function TJSONUnMarshal.CreateObject(JsonObj: TJSONObject): TObject;
var
objType: string;
ObjId: string;
objFields: TJSONObject;
Obj: TObject;
rttiType : TRttiType;
attr : TCustomAttribute;
customizer : TJSONPopulationCustomizer;
JsonPairID: TJSONPair;
JsonPairType: TJSONPair;
JsonPairFields: TJSONPair;
JsonPairRefName: TJSONPair;
begin
assert(JsonObj <> nil);
assert(JsonObj.Count > 1);
JsonPairID := JsonObj.Get(ID_NAME);
if JsonPairID <> nil then
begin
JsonPairType := JsonObj.Get(TYPE_NAME);
JsonPairFields := JsonObj.Get(FIELDS_NAME);
Assert(JsonPairFields <> nil);
Assert(JsonPairType <> nil);
objType := JsonPairType.JsonValue.Value;
ObjId := JsonPairID.JsonValue.Value;
objFields := TJSONObject(JsonPairFields.JsonValue);
Obj := ObjectInstance(FRTTICtx, objType); // returns nil, objtype is wrong
if Obj = nil then
raise EConversionError.Create(Format(SCannotCreateType, [objType]));
In My Servermethodsclient I changed the Type of my parameter. It works but it looks more like a quick fix to me and i wish i could automate it. If i had plenty of servermethods like this this would be tedious work, but for now it is only one method to authenticate on a server.
pJSON := TJSONObject( FPW_GetServerInfoCommand.Parameters[2].Value.GetJSONValue(True));
pPair := pJSON.RemovePair('type');
pPair.Free;
pJSON.AddPair(TJSONPair.Create('type', 'NewUnit.TMyObject'));
Result := TMyObject(FUnMarshal.UnMarshal(FPW_GetServerInfoCommand.Parameters[2].Value.GetJSONValue(True)));

Get specific Value from Json String Delphi XE8

I have this Json String in Delphi,
{
"bpd": {
"euro": {
"buying_rate": "48.50",
"selling_rate": "52.70"
},
"dollar": {
"buying_rate": "45.30",
"selling_rate": "45.80"
},
"source": "https://www.popularenlinea.com/_api/web/lists/getbytitle('Rates')/items"
},
"blh": {
"euro": {
"buying_rate": "48.50",
"selling_rate": "52.00"
},
"dollar": {
"buying_rate": "45.35",
"selling_rate": "45.80"
},
"source": "http://www.blh.com.do/Inicio.aspx"
}
}
I want extract the buying_rate and selling_rate for the dollar for the bank blh
i try this but i get AV
var
LJsonObj : TJSONObject;
LRows, LElements, LItem : TJSONValue;
begin
LJsonObj := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(s),0) as TJSONObject;
try
LRows:=LJsonObj.Get(0).JsonValue;
LElements:=TJSONObject(TJSONArray(LRows).Get(0)).Get(0).JsonValue;
LItem :=TJSONObject(TJSONArray(LElements).Get(0)).Get(0).JsonValue;
ShowMessage(TJSONObject(LItem).Get('buying_rate').JsonValue.Value);
finally
LJsonObj.Free;
end;
There are many errors in your code. The most significant is your repeated use of unchecked casts. When you write
TJSONArray(LRows)
you are telling the compiler that you know 100% for sure that LRows is descended from TJSONArray. Well, it isn't. Especially when you are dealing with external data you must not make such assumptions. You are then subject to the whims of the data that you receive. Use a checked cast instead
LRows as TJSONArray
Now, that's still wrong because LRows is not an array. In fact your JSON doesn't have any arrays at all. It just has objects. But when you use the checked cast the failure will be a meaningful error rather than an access violation.
This program reads the values you are looking for:
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.JSON, System.IOUtils;
procedure Main;
var
s: string;
LJsonObj: TJSONObject;
blh: TJSONObject;
dollar: TJSONObject;
rate: TJSONString;
begin
s := TFile.ReadAllText('C:\desktop\json.txt');
LJsonObj := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(s), 0) as TJSONObject;
try
blh := LJsonObj.GetValue('blh') as TJSONObject;
dollar := blh.GetValue('dollar') as TJSONObject;
rate := dollar.GetValue('buying_rate') as TJSONString;
Writeln(rate.Value);
rate := dollar.GetValue('selling_rate') as TJSONString;
Writeln(rate.Value);
finally
LJsonObj.Free;
end;
end;
begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Output
45.35
45.80
I advise you to spend some time at the JSON site to make sure that you have a very clear understanding of the terminology. You should have a clear understanding of what is meant by the terms object, array and value. At the moment I think that is lacking.
If you use jsonDoc it would look something like this:
StrToFloat(JSON(JSON(JSON(TFile.ReadAllText('C:\desktop\json.txt'))['blh'])['dollar'])['buying_rate'])

How to parse a json string response using Delphi

I have a rest server returning the next json string:
response:='{"result":["[{\"email\":\"XXX#gmail.com\",\"regid\":\"12312312312312312313213w\"},{\"email\":\"YYYY#gmail.com\",\"regid\":\"AAAAAAA\"}]"]}';
I´d like to parse the response to get a list of all email and regid items.
I have tried the next code but I am getting an AV at (TJSONPair(LItem).JsonString.Value='email')
Any help will be appreciated.
Thanks in advance, Luiz
var
LResult:TJSONArray;
LJsonresponse:TJSONObject;
i:integer;
LItem,jv:TJsonValue;
email,regid:string;
LJsonresponse:=TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(response),0) as TJSONObject;
LResult:=(LJsonresponse.GetValue('result') as TJSONArray);
jv:=TJSONArray(LResult.Get(0));
for LItem in TJSONArray(jv) do begin
if (TJSONPair(LItem).JsonString.Value='email') then begin
email:=TJSONPair(LItem).JsonValue.Value;
end;
if (TJSONPair(LItem).JsonString.Value='regid') then begin
regid:=TJSONPair(LItem).JsonValue.Value;
end;
end;
Your problems start here:
jv := TJSONArray(LResult.Get(0));
The problem is that LResult.Get(0) does not return an instance of TJSONArray. In fact it returns an instance of TJSONString. That string has value:
'[{"email":"XXX#gmail.com","regid":"12312312312312312313213w"},{"email":"YYYY#gmail.com","regid":"AAAAAAA"}]'
It looks like you are going to need to parse this string as JSON to extract what you need. Here is some gnarly code that does that. Please excuse its quality because I have no experience at all with the Delphi JSON parser.
{$APPTYPE CONSOLE}
uses
SysUtils, JSON;
const
response =
'{"result":["[{\"email\":\"XXX#gmail.com\",\"regid\":\"12312312312312312313213w\"},'+
'{\"email\":\"YYYY#gmail.com\",\"regid\":\"AAAAAAA\"}]"]}';
procedure Main;
var
LResult: TJSONArray;
LJsonResponse: TJSONObject;
ja: TJSONArray;
jv: TJSONValue;
begin
LJsonResponse := TJSONObject.ParseJSONValue(response) as TJSONObject;
LResult := LJsonResponse.GetValue('result') as TJSONArray;
ja := TJSONObject.ParseJSONValue(LResult.Items[0].Value) as TJSONArray;
for jv in ja do begin
Writeln(jv.GetValue<string>('email'));
Writeln(jv.GetValue<string>('regid'));
end;
end;
begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
The big lesson here is to stop using unchecked type casts. Using such casts is asking for trouble. When your data does not match your code, you get unhelpful error messages.

Delphi. Parse JSON

I'm trying to parse JSON string (http://pastebin.com/zXn8pwtL):
sListing contains JSON string
jsObj: TJSONObject;
jsItem, jsElem, jsAsserts: TJSONValue;
...
jsObj := TJSONObject.ParseJSONValue(sListing) as TJSONObject;
jsAsserts := TJSONObject(TJSONObject(jsObj.Get('assets').JsonValue).Get('730').JsonValue).Get('2').JsonValue;
for jsElem in TJSONArray(jsAsserts) do
WriteLn(jsElem.ToString);
How can I get all values from jsElem->descriptions? I've tried to enum all:
for jsItem in TJSONArray(jsElem) do
WriteLn(jsItem.ToString);
But got EAccessViolation Read of address 00000001
Ok. Thanks for all. It was some misunderstanding with JSON types. It is fixed now:
jsAsserts: TJSONObject;
jsDesc: TJSONArray;
iCurEl: integer;
jsItem: TJSONPair;
...
jsAsserts := TJSONObject(TJSONObject(jsObj.Get('assets').JsonValue).Get('730').JsonValue).Get('2').JsonValue as TJSONObject;
for iCurEl := 0 to jsAsserts.Count - 1 do
begin
jsItem := jsAsserts.Pairs[iCurEl];
jsDesc := (jsItem.JsonValue as TJSONObject).Get('descriptions').JsonValue as TJSONArray;
WriteLn(jsDesc.ToString);
end;