parse JSON withi UTF-8 Value in delphi xe3 with TlkJSONobject - json

I wanna parse JSON than have UTF-8 string but when I show this value in delphi XE3 in a label just show ???? but when show in ShowMessage(); this value is correct please help me
value: 'سعید'
my code:
procedure TServerMethods1.Ins_Info(var TehResult: String);
var
name: string;
js,xs:TlkJSONobject;
begin
js := TlkJSON.ParseText(ThResult) as TlkJSONobject;
if not assigned(js) then
begin
readln;
exit;
end
else
begin
name := AnsiToUtf8(js.getString('name'));
end;
end;

According to the source of lkJSON unit, there is an unnecessary Decode from UTF8 :
{$ifdef USE_D2009}
js.FValue := UTF8ToString(ws);
{$else}
js.FValue := UTF8Decode(ws);
{$endif}
If your JSON Strings are not encoded in UTF8, find and comment this lines and just assign ws value to the js.FValue :
...
js := TlkJSONstring.Create;
//{$ifdef USE_D2009}
// js.FValue := UTF8ToString(ws);
//{$else}
// js.FValue := UTF8Decode(ws);
//{$endif}
js.FValue := ws;
...
and there is no need to use AnsiToUtf8 or such methods, just use getString :
name := js.getString('name');

Related

Parsing a JSON string that contains an array of an array of a string of another jsonstring in Delphi

So I'm talking to this webserver, and it's returning me a json entry like this:
{
"result": [
[],
["{\"success\": \"true\", \"Message\":\"User 1 has been deleted.\"}"]
]
}
{"result":[[],["{\"success\": \"true\", \"Message\":\"User 1 has been deleted.\"}"]]}
And I'm having trouble getting things out of it.
Looking at it, I made a jsonobject, pulled the value of result and made it an array, then pulled the second entry of the first array as another array, then took that jsonstring and turned it into another jsonarray, then took the values out.
but for some reason the first jsonarray claims to have two values, both of which are empty. I'm sure there are other errors in my approach past that point as well.
Can I get a hand ironing this thing out?
procedure DeleteUser;
var
aJSON, aResult : String;
aJsonResponse : TJsonObject;
aResultArrayA : TJsonArray;
aResultArrayB : TJsonArray;
aResultArrayC : TJsonArray;
aParsed : TJsonValue;
i : Integer;
Begin
aresult := '{"result":[[],["{\"success\": \"true\", \"Message\":\"User 1 has been deleted.\"}"]]}';
aJsonResponse := TJsonObject.ParseJSONValue(aResult) as TJsonObject;
if not (aJsonResponse is TJsonObject) then
raise Exception.Create('InvalidResponse');
aResultArrayA := aJsonResponse.getValue('result') as TJsonArray;
if aResultArrayA.Count <= 0 then //is 2
raise Exception.Create('InvalidResponse');
aJSON := aResultArrayA.Items[0].Value; // is ''
aJSON := aResultArrayA.Items[1].Value; // is ''
aResultArrayB := aResultArrayA.Items[0] as TJSONArray;
if aResultArrayB.Count <= 0 then // is 0
raise Exception.Create('InvalidResponse'); //raises here
aJSON := aResultArrayB.Items[0].Value;
aJSON := aResultArrayB.Items[1].Value;
aResultArrayC := TJSONObject.ParseJSONValue(aResultArrayB.Items[1].Value) as TJSONArray;
for aParsed in aResultArrayC do begin
aJson := aJson + aParsed.GetValue<string>('success') + ' ';
aJson := aJson + aParsed.GetValue<string>('message') + ' ';
end;
end;
Thanks everyone.
I really think that the best way to work with JSON is serialization and deserialization. Yes, there is some situations when it's better to use parsing, but look at this:
uses ...,Rest.Json;
TMyArray = ARRAY of ARRAY of string;
//class for deserialization outer JSON object
TMyParse = CLASS
private
FResult:TMyArray;
procedure SetResult(const Value: TMyArray);
public
property result:TMyArray read FResult write SetResult;
END;
//class for deserialization inner JSON object
TMyInnerParse = class
private
FSuccess:Boolean;
FMessage:string;
procedure SetMessage(const Value: String);
procedure SetSuccess(const Value: Boolean);
public
property success:Boolean read FSuccess write SetSuccess;
property message:String read FMessage write SetMessage;
end;
procedure DeleteUser;
var
OuterObj: TMyParse;
InnerObj: TMyInnerParse;
aResult: String;
i,j: Integer;
Begin
aResult := '{"result":[[],["{\"success\": \"true\", \"Message\":\"User 1 has been deleted.\"}"]]}';
try
OuterObj := TJson.JsonToObject<TMyParse>(aResult);
if Length(OuterObj.result) > 0 then
for i := 0 to Length(OuterObj.result) - 1 do
if length(OuterObj.result[i]) > 0 then
for j := 0 to Length(OuterObj.result[i]) - 1 do
begin
try
InnerObj := TJson.JsonToObject<TMyInnerParse>(OuterObj.result[i][j]);
//Do your work with result, that in InnerObj now
finally
if assigned(InnerObj) then
FreeAndNil(InnerObj);
end;
end;
finally
if assigned(OuterObj) then
FreeAndNil(OuterObj);
end;
end;
procedure TMyParse.SetResult(const Value: TMyArray);
begin
FResult := value;
end;
procedure TMyInnerParse.SetMessage(const Value: String);
begin
FMessage := value;
end;
procedure TMyInnerParse.SetSuccess(const Value: Boolean);
begin
FSuccess := value;
end;
For cycles in this code are awful, but it's the fastest way to show how you can solve your problem. And it's working.
I don't know what information can be in first empty array and this can be the reason for exceptions. Look at this code as working example, not full solution because lack of information.
It was tested on Delphi 10.1:
P.S. Using arrays are very old way of coding in this situation. But some time ago I met problem with serializing/deserializing TList and TObjectList. I'll try to use them and will return with result.
P.P.S. It tried to use TList, but my attempt fails. Maybe someone can describe how to implement it in a code above.
Found these functions in system.JSON and they just clicked for me.
/// <summary>Finds a JSON value and returns reference to it. </summary>
/// <remarks> Raises an exception when a JSON value could not be found. </remarks>
property P[const APath: string]: TJSONValue read GetValueP;{ default;}
property A[const AIndex: Integer]: TJSONValue read GetValueA;
var
aSuccess, aMessage : String
aJSON : TJSONObject;
begin
var aJSON:= TJSONObject.ParseJSONValue('{"result":[[],["{\"success\": \"true\", \"Message\":\"User has been deleted.\"}"]]}');
aSuccess := TJSONObject.ParseJSONValue(aJSON.P['result'].A[1].A[0].AsType<String>).P['success'].AsType<String>;
aMessage := TJSONObject.ParseJSONValue(aJSON.P['result'].A[1].A[0].AsType<String>).P['Message'].AsType<String>;
end;
Note that this needs exception handling, all of these functions will throw an exception if they fail to find the specified property.

delphi what is difference of [ and { in json array

I have a JSON string and need to parse it
JsonString :='{"uid":"1","full_name":"test","user_name":"test","mobile":"0999","send_sms":""' +',"recieve_sms":"","mob_app":"","password":"test","email":"","credit":"0.00","status":"agent","add_date":"2020-01-04 13:05:32","agent":"0","theme":""}';
LJsonArr := TJSONObject.ParseJSONValue(JsonString) as TJSONArray;
for LJsonValue in LJsonArr do
begin
for LItem in TJSONArray(LJsonValue) do begin
memo1.Lines.Add(Format('%s : %s',[TJSONPair(LItem).JsonString.Value, TJSONPair(LItem).JsonValue.Value]));
end;
end;
this not working but if I put JSON string in a [] the code work well.
what is the difference of [{}] with {} or [] and how can I process my string with just {}
Your code doesn't work because the JSON in question does not contain any arrays at all, so all of your typecasts to TJSONArray are wrong. The JSON represents a single object (a TJSONObject) containing name/value pairs of strings, nothing more.
By surrounding the JSON with [], you create an array that contains 1 element, an object. So your outer loop is satisfied, but your inner loop is still wrong since it would need to typecast LJsonValue to TJSONObject rather than TSONArray.
To process the original JSON correctly, try this instead:
JsonString :='{"uid":"1","full_name":"test","user_name":"test","mobile":"0999","send_sms":""' +',"recieve_sms":"","mob_app":"","password":"test","email":"","credit":"0.00","status":"agent","add_date":"2020-01-04 13:05:32","agent":"0","theme":""}';
LJsonValue := TJSONObject.ParseJSONValue(JsonString);
if LJsonValue <> nil then
try
LJsonObj := LJsonValue as TJSONObject;
for LJsonPair in LJsonObj do
begin
Memo1.Lines.Add(Format('%s : %s',[LJsonPair.JsonString.Value, LJsonPair.JsonValue.Value]));
end;
finally
LJsonValue.Free;
end;

JSON to XML using XSLT 3.0 - how to load JSON source and call json-to-xml function?

I want to experiment (in Delphi code) with the XSLT 3.0 and its json-to-xml() function:
In XSLT 3.0, an inbound document can be in JSON, rather than XML. The processor can take that document, use the json-to-xml() function to convert it into a specific known XML format, process that through the templates, then convert the resulting output back into JSON (or can convert it into HTML 5 among other formats
But I'm stuck in two places:
How do I use a JSON string as the source for the transform? Trying to loading it into a TXMLDocument gives me (of course?) 'malformed' errors
How would I then apply the "json-to-xml() function". All the examples I find about using XSLT transforms in Delphi use the TransformNode function, as the below code.
Things like lDoc.Node.json-to-xml do not compile.
.
var
lDoc, lXSL, lRes: IXMLDocument;
lUTF8Str : UTF8String;
begin
lDoc := LoadXMLData(AXMLString);
lXSL := LoadXMLData(cRemoveNSTransform);
lRes := NewXMLDocument;
lDoc.Node.TransformNode(lXSL.Node,lRes); // Param types IXMLNode, IXMLDocument
lRes.SaveToXML(lUTF8Str);
Can anyone point me in the right direction?
I'm going to write a 'guide' to my own question that does not use XSLT but instead uses the IP*Works! Delphi components that we have a subscription for.
This may at least give others an available option, or a rough idea how to 'roll your own'.
We use the IP*Works! TipwJSON and TipwXML components.
The trick is to intercept the parsing of the JSON component and then write the detected data to the XML component.
This is code from a test app showing how we did it (I have left logging code in):
TJSONTOXML = class(TIpwJSON)
private
FXML : TipwXML;
FLogLevel : Integer;
procedure ShowLogLine(AMsg: String);
procedure InterceptJSONStartElement(Sender: TObject; const Element: string);
procedure InterceptJSONEndElement(Sender: TObject; const Element: string);
procedure InterceptCharacters(Sender: TObject; const Text: string);
function GetXML: String;
public
property XML: String read GetXML;
constructor Create(AOwner: TForm; ALogLevel: Integer); overload; // For now testing on a Form
end;
constructor TJSONTOXML.Create(AOwner: TForm; ALogLevel: Integer);
begin
inherited Create(AOwner);
FLogLevel := ALogLevel;
Self.BuildDOM := false;
Self.OnStartElement := InterceptJSONStartElement;
Self.OnEndElement := InterceptJSONEndElement;
Self.OnCharacters := InterceptCharacters;
FXML := TipwXML.Create(nil);
end;
procedure TJSONTOXML.InterceptJSONEndElement(Sender: TObject; const Element: string);
begin
if Element = '' then // End of array
begin
if FLogLevel > 2 then ShowLogLine('JSON parse EndElement - Array');
FXML.EndElement;
end
else
begin
if FLogLevel > 2 then ShowLogLine('JSON parse EndElement - Element: ' + Element);
FXML.EndElement;
end;
end;
procedure TJSONTOXML.InterceptJSONStartElement(Sender: TObject; const Element: string);
begin
if Element = '' then // Start of array
begin
if FLogLevel > 2 then ShowLogLine('JSON parse StartElement - Array');
FXML.StartElement('ARRAY','');
end
else
begin
if FLogLevel > 2 then ShowLogLine('JSON parse StartElement - Element: ' + Element);
FXML.StartElement(Uppercase(Element),'');
end;
end;
procedure TJSONTOXML.ShowLogLine(AMsg: String);
// Use WM_COPYDATA to send log info to form
var CopyDataStruct: TCopyDataStruct;
begin
CopyDataStruct.dwData := 0;
CopyDataStruct.cbData := 2 + 2 * Length(AMsg);
CopyDataStruct.lpData := PChar(AMsg);
SendMessage((Owner as TForm).Handle, WM_COPYDATA, (Owner as TForm).Handle, lParam(#CopyDataStruct));
end;
function TJSONTOXML.GetXML: String;
begin
FXML.EndElement;
Result := FXML.OutputData;
end;
procedure TJSONTOXML.InterceptCharacters(Sender: TObject; const Text: string);
var lText: String;
begin
// Always surrounded by quotes, remove:
lText := StripQuotes(Text);
if FLogLevel > 2 then ShowLogLine('JSON parse characters: ' + lText);
FXML.PutString(lText);
end;
With this you can
lJSONToXML := TJSONTOXML.Create(Self,FDataLogLvl);
// Get your JSON data from somewhere, e.g. a HTTP component. Then:
lJSONToXML.Inputdata := lData;
lJSONToXML.Parse; // The Parse method initiates the parsing that was postponed by setting BuildDom := false
// The XML is now in the OutputData property of the TipwXML and can e.g. be retrieved by our:
lOutputData := lJSONToXML.XML;
Note that:
There is no namespace information in the XML
The JSON arrays when converted to XML are converted to nodes named ARRAY
All data is kept in memory

Parsing nullable TJSONObject with Delphi

I'm using Delphi XE3. I have a JSON stream where an object can be null. That is, I can receive:
"user":null
or
"user":{"userName":"Pep","email":"pep#stackoverflow.com"}
I want to discriminate both cases, and I tried with this code:
var
jUserObject: TJSONObject;
jUserObject := TJSONObject(Get('user').JsonValue);
if (jUserObject.Null)
then begin
FUser := nil;
end else begin
FUser := TUser.Create;
with FUser, jUserObject do begin
FEmail := TJSONString(Get('email').JsonValue).Value;
FUserName := TJSONString(Get('userName').JsonValue).Value;
end;
end;
If I put a breakpoint right in line if (jUserObject.Null) then begin and I mouse over jUserObject.Null it says jUserObject.Null = True if "user":null and it says jUserObject.Null = False if "user":{"userName":"Pep","email":"pep#stackoverflow.com"}
However, if I step into that line with the debugger, jUserObject.Null calls the following XE3 library code:
function TJSONAncestor.IsNull: Boolean;
begin
Result := False;
end;
So I always get a False for my if sentence, even if "user":null.
I suppose I always have the workaround of catching the exception that is raised when "user":null and Get('email').JsonValue is executed in order to discriminate if the value is null or not, but that does not seem so elegant.
How is one supposed to detect if an JSON object has a null value in the JSON stream?
Get() returns a TJSONPair. When you have "user":null, the TJSONPair.JsonValue property will return a TJSONNull object, not a TJSONObject object. Your code is not accounting for that possibility. It assumes the JsonValue is always a TJSONObject and not validating the type-cast.
There are two ways to handle this.
TJSONPair has its own Null property that specifies whether its JsonValue is a null value or not:
var
JUser: TJSONPair;
jUserObject: TJSONObject;
jUser := Get('user');
if jUser.Null then begin
FUser := nil;
end else begin
// use the 'as' operator for type validation in
// case the value is something other than an object...
jUserObject := jUser.JsonValue as TJSONObject;
...
end;
Use the is operator to test the class type of the JsonValue before casting it:
var
JUser: TJSONPair;
jUserObject: TJSONObject;
jUser := Get('user');
if jUser.JsonValue is TJSONNull then begin
FUser := nil;
end
else if jUser.JsonValue is TJSONObject then begin
jUserObject := TJSONObject(jUser.JsonValue);
...
end else begin
// the value is something other than an object...
end;
You've made the common mistake of confusing JSON objects with Delphi objects. The TJSONObject class represents JSON objects only, which are never null because null is distinct from {...}. TJSONObject is not the ancestor for all JSON values, like your code assumes. TJSONValue is.
Don't type-cast your "user" value to TJSONObject until you know it's an object. Check the Null property first, then type-cast.

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.