Delphi Alexandria - Backward slash in JSON response - json

In JSON response, backward slashes are included in string if we have forward slash character.
I am using the below code to get the API response.
procedure GetJSONInformation;
var
objResponse : TRESTResponse;
objClient : TRESTClient;
objRequest : TRESTRequest;
sJSONResponse : string;
begin
objResponse := TRESTResponse.Create(nil);
objClient := TRESTClient.Create(nil);
objClient.Accept := 'application/json, text/plain; q=0.9, text/html;q=0.8,';
objClient.AcceptCharset := 'UTF-8, *;q=0.8';
objRequest := TRESTRequest.Create(nil);
objRequest.Method := rmGET;
objRequest.Accept := 'application/json, text/plain; q=0.9, text/html;q=0.8,';
objRequest.AcceptCharset := 'UTF-8, *;q=0.8';
objRequest.Client := objClient;
objRequest.Response:= objResponse;
try
objClient.BaseURL := 'https://sample.net';
ObjRequest.Resource := 'api/GetInformation/{Param1}/{Param2}';
ObjRequest.AddParameter('Param1', 'Parameter1', TRESTRequestParameterKind.pkURLSEGMENT);
ObjRequest.AddParameter('Param2', 'Parameter2', TRESTRequestParameterKind.pkURLSEGMENT);
ObjRequest.Execute;
if ObjResponse.StatusCode = 200 then
sJSONResponse:= ObjResponse.JsonText; //Here i got the JSON response
finally
FreeAndNil(objRequest);
FreeAndNil(objClient);
FreeAndNil(objResponse);
end;
end;
In API Response, backward slashes are included in string if I had forward slash in it. For Example,
JSON Response: "Date": "04\/13\/2022",
"stringdata": "DEC\/ACB test",
Expected Response: "Date": "04/13/2022",
"stringdata": "DEC/ACB test",
This is happening only in Alexandria version of Delphi whereas it was working fine in Delphi Berlin.
All I want to remove the backward slash in string. Please help me

You don't have to remove anything.
Just parse valid json response and you will obtain the correct field value without escaped json string, just like #Dave Nottage and #Adriaan mentioned in comments.
See below two alternatives to parse with System.json and mORMot. It was tested with Delphi 10.x and Delphi 11 versions.
program Test_SystemJson_and_mORMot;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.JSON,
Syncommons;
const
ValidJSON='{"Date": "04\/13\/2022","stringdata": "DEC\/ACB test"}';
var
// with System.JSON
JsonValue : TJSonValue;
// with mORMot
Json : variant;
begin
// with System.JSON
JsonValue := TJSonObject.ParseJSONValue(ValidJSON);
writeln(JsonValue.GetValue<string>('Date')); // Result 04/13/2022
writeln(JsonValue.GetValue<string>('stringdata')); // Result DEC/ACB test
// with mORMot
Json := _Json(ValidJSON);
writeln(Json.Date); // Result 04/13/2022
writeln(Json.stringdata); // Result DEC/ACB test
readln;
end.

Related

Delphi TRESTRequest gets Access Violation when attempting TRESTRequest.AddBody()

I am working with a Delphi (10.2.2) Client app that calls API functions on a Delphi REST Server (WebModule). At this point I have 10s of these types of API calls that perform all relevant HTTP verbs (POST, GET, PUT, DELETE) and they all work fine, EXCEPT for calls where I need to upload a JSON object for INSERT/EDITs on the Server. I am not sure what I am doing wrong, so if anyone could look at my attempt and let me know what I'm missing?
var
REQ : TRESTRequest;
RESP : TRESTResponse;
NewPL : TJSONObject;
strOut : String;
begin
try
REQ := TRESTRequest.Create(Application);
RESP := TRESTResponse.Create(Application);
REQ.Response := RESP;
REQ.Resource := 'packlist';
REQ.Client := DM1.RESTClient;
REQ.Method := TRESTRequestMethod.rmPOST;
//Create JSON from UI
NewPL := TJSONObject.Create;
NewPL.AddPair('PL_NO', edtPLNo.Text);
NewPL.AddPair('PL_DATE', edtPLDate.Text);
NewPL.AddPair('SHIP_NO', edtShipmentNo.Text);
NewPL.AddPair('SHIPPER', memShipper.Text);
NewPL.AddPair('CONSIGNEE', memConsignee.Text);
NewPL.AddPair('VEHICLE_REG_NO', edtVehicleRegNo.Text);
NewPL.AddPair('ETD', DateTimeToStr(dateETD.Date));
NewPL.AddPair('ETA', DateTimeToStr(dateETA.Date));
NewPL.AddPair('TOTAL_QTY', edtTotalQty.Text);
NewPL.AddPair('TOTAL_WEIGHT', edtTotalWeight.Text);
NewPL.AddPair('TOTAL_VOLUME', edtTotalVol.Text);
if chkPLComplete.Checked then
NewPL.AddPair('COMPLETE', '1')
else
NewPL.AddPair('COMPLETE', '0');
NewPL.AddPair('SUPPLIER_NO', edtSuppNo5.Text);
NewPL.AddPair('DEBTOR_ACC_NO', '');
NewPL.AddPair('WAYBILL_NO', edtWaybillNo.Text);
strOut := NewPL.ToString;
REQ.AddBody(NewPL); //AV occurs here
REQ.Execute;
DisplayPL(NewPL);
finally
REQ.DisposeOf;
RESP.DisposeOf;
end;
end;
I am sure I have made a mistake somewhere, that I keep overlooking...

I need to know where I'm wrong in json object to POST using TIdHTTP in Delphi

source gives socket error 14001, WITH OBJ JSON PARAM MESSAGE FOR POST
jso := TlkJSONobject.Create; // (data) as TlkJSONobject;
jso.Add('InvoiceNumber', '');
jso.Add('POSID', '910441');
jso.add('USIN', ePOSNo.Text);
jso.add('DATETIME', eDate.Text);
IdHTTP1.Request.Accept := 'application/json';
IdHTTP1.Request.ContentType := 'application/json';
{ Call the Post method of TIdHTTP and read the result into TMemo }
Memo1.Lines.Text := IdHTTP1.Post('http://localhost;8524/api/IMSFISCAL/GetInvoiceNumberByModel', JSO);
json cannot be passed as tstream
need help on it
There is no way the code you showed can produce a socket error (let alone error 14001, which is not even a socket error) since the code won't even compile!
The TIdHTTP.Post() method does not have an overload that accepts a TlkJSONobject as input. How could it? TlkJSONobject comes from a completely different 3rd party library, it is not part of the RTL or Indy. The only things you can POST with TIdHTTP are:
TStrings-derived types
TStream-derived types, including Indy's TIdMultiPartFormDataStream
a file specified by a String filename
In this case, you need to use a TStream to post JSON stored in memory. It is your responsibility to save your TlkJSONobject content to a suitable TStream of your choosing. That is outside the scope of Indy. For instance, you can use TlkJSON.GenerateText() to get the JSON into a String and then POST it using a TStringStream.
On a side note, the URL you are passing to TIdHTTP.Post() is malformed. The correct delimiter between a hostname and port number is a colon (:), not a semicolon (;).
With that said, try this:
jso := TlkJSONobject.Create;
jso.Add('InvoiceNumber', '');
jso.Add('POSID', '910441');
jso.add('USIN', ePOSNo.Text);
jso.add('DATETIME', eDate.Text);
IdHTTP1.Request.Accept := 'application/json';
IdHTTP1.Request.ContentType := 'application/json';
{ Call the Post method of TIdHTTP and read the result into TMemo }
PostData := TStringStream.Create(TlkJSON.GenerateText(jso), TEncoding.UTF8);
try
Memo1.Lines.Text := IdHTTP1.Post('http://localhost:8524/api/IMSFISCAL/GetInvoiceNumberByModel', PostData);
finally
PostData.Free;
end;

Json Parse "Invalid class typecast" issue. [Delphi XE7]

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

How to encode output json file (SuperObject)?

I'm using SuperObject library for working with JSON.
This code creates JSON:
procedure TfmMain.btnIngredientsSaveClick(Sender: TObject);
var obj: ISuperObject;
i: integer;
begin
try
obj := SO();
for i := 0 to sgIngredients.RowCount - 2 do
begin
obj.O[sgIngredients.Cells[0, i+1]] := SA([]);
obj.A[sgIngredients.Cells[0, i+1]].S[0] := sgIngredients.Cells[1, i+1];
obj.A[sgIngredients.Cells[0, i+1]].S[1] := sgIngredients.Cells[2, i+1];
end;
obj.SaveTo(ExtractFileDir(Application.ExeName)+ingrJSONFile);
finally
obj := nil;
end;
end;
sgIngredients - TStringGrid
sgIngredients contain cyrillic symbols. So output file is:
{
"4":["Hello","count"],
"3":["\u0411\u0443\u043b\u044c\u043e\u043d \u043e\u0432\u043e\u0449\u043d\u043e\u0439","\u0441\u0442."],
"2":["\u0411\u0443\u043b\u044c\u043e\u043d \u043a\u0443\u0440\u0438\u043d\u044b\u0439","\u0441\u0442."],
"1":["\u0411\u0435\u043a\u043e\u043d","\u0433\u0440."]
}
How to correctly save my data to JSON-file?
EDIT
This is screenshot of my string grid.
Reading the sources, you can call function TSuperObject.SaveTo(stream: TStream; indent, escape: boolean): integer; setting escape := false
I can say it again, when using libraries with their source code given, just "Use the Source, Luke"
Also, u may save JSON to string, and then replace escaped characters with actual WideChar values (like was done in http://UniRed.sf.net or at http://www.sql.ru/forum/936760/perevesti-kodirovannye-simvoly-funkciya-v-delphi-analog-iz-js) and then save the resulting string to file while enforcing UTF-8 charset.

Extracting/parsing data returned as JSON from PHP

I'm getting JSON data back from a PHP call using TIdHTTP. I'm expecting to use TJSONObject but can't work out how to use it for this purpose. And there are no examples in the XE3 help.
Sample JSON data
[{"fleet_id":"2","fleet":"EMB195"},{"fleet_id":"3","fleet":"EMB175"},{"fleet_id"‌:"1","fleet":"DHC84-400Q"}]
I'm sure this is simple, but how!
Thanks.
Use TJsonObject.ParseJsonValue to convert the input string into a JSON value:
var
val: TJsonValue;
s := '[{"fleet_id":"2","fleet":"EMB195"},{"fleet_id":"3","fleet":"EMB175"},{"fleet_id"‌:"1","fleet":"DHC84-400Q"}]';
val := TJsonObject.ParseJsonValue(s);
In this case, the JSON happens to represent an array, so you can type-cast it to that:
var
arr: TJsonArray;
arr := val as TJsonArray;
You can access the elements of the array with Get, and type-cast the results to TJsonObject.
var
i: Integer;
elem: TJsonObject;
for i := 0 to Pred(arr.Size) do begin
elem := arr.Get(i) as TJsonObject;
end;
To inspect the properties of the object, you can use the Get method, which returns a TJsonPair holding the name and value.