Delphi, TSuperObject > from JSON to Object - json

I have in Delphi XE4, TSuperObject:
TAspTransactionBasicData = Class(TObject)
Currency : Byte;
Amount : Currency;
constructor Create(aCurrency: Byte; aAmount: Currency);
end;
TStartWorkflowWithBasicData = Class(TObject)
AdditionalData : TAspTransactionBasicData; // here it is as an Object
TypeOfWorkflow : Byte;
constructor Create(aAdditionalData: TAspTransactionBasicData; aTypeOfWorkflow: Byte);
function toJSon(TObject:TStartWorkflowWithBasicData):String;
end;
and the ToJSon function puts the object into JSON:
function TStartWorkflowWithBasicData.toJSon(TObject:TStartWorkflowWithBasicData):String;
Var
JSon: ISuperObject;
RttiCont: TSuperRttiContext;
begin
Result := '';
RttiCont := TSuperRttiContext.Create;
JSon := RttiCont.AsJson<TStartWorkflowWithBasicData>(TObject); // Insert the object into JSON
Result := JSon.AsJSon(False);
RttiCont.Free;
end;
and I would also need the exact opposite, a function fromJSon, which will read the JSON (where the object was inserted) back into the object, but I'm groping...
Can you please advise me?

I can't help you with SuperObject, but kbmMW contains a very complete XML, JSON, YAML, BSON, MessagePack + CSV and in next release also TXT (fixed format) serializer/deserializer and object marshaller/unmarshaller.
It will easily convert both ways, and even between formats.
kbmMW Community Edition contains all (except the new TXT format), and is free, even for commercial use (limited by license).
https://components4developers.blog/2019/03/11/rest-easy-with-kbmmw-24-xml_json_yaml_to_object_conversion/

Related

Delphi 2007 JSON array of string to TStringList

Embarcadero introduced JSON types with Delphi 2009, but I'm using Delphi 2007 without these JSON types.
I'm wanting to process a JSON string being an "array of strings" into a TStringList object.
e.g.
["Ford", "BMW", "Fiat"]
I guess it's string token type of processing ..
There is a third party Delphi unit that might be useful for this:
https://sourceforge.net/projects/lkjson/
uses uLkJSON;
var
AString:String;
AStringList:TStringList;
jsonbase,Items : TlkJSONbase; //as per 3rd party unit uLkJSON
I: Integer;
begin
AStringList:=TStringList.create;
AString := '["Ford", "BMW", "Fiat"]';
jsonbase := TlkJSON.ParseText(AString);
if Assigned(jsonbase) then
begin
for I := 0 to Pred(jsonbase.Count) do
begin
Items := jsonbase.Child[I];
AStringList.add(Items.Value);
end;
end;
Result := AStringList;
end;

How to declare a POST request in Delphi REST Datasnap?

I'm trying to write a function that will insert informations in my database using JSON or multiple parameters in the URL.
I already managed to create a function that returns informations from my database to the client but not the other way. Every example I find explains how to creat a JSON but not how to receive it with System.JSON (Delphi 10.2)
Here is a code sample for a GET function actually working on my Datasnap :
function TDM_Test.DS_getArticles(pStock : String): string;
begin
try
VGOutils.PR_logs('Get all articles from table');
VGOutils.FU_CheckConnect('1234567890');
with VGOutils.SQ_temp1 do
begin
SQL.Clear;
SQL.Add('SELECT code, name, price, seller, departement FROM articles');
SQL.Add('WHERE stock');
ParamByName('stock').AsString := pStock;
VGOutils.SQ_temp1.Open;
end;
GetInvocationMetadata().ResponseCode := 200;
GetInvocationMetadata().ResponseContent :=
VGOutils.PR_JSON(VGOutils.SQ_temp1, ['code', 'name', 'price', 'seller']);
except on E: Exception do
PR_error(E.Message);
end;
VGOutils.PR_Disconnect;
end;
Now I need the client to send me new articles to INSERT in my database.
The only thing that I cant figure out is how to declare the function and it's parameters.
I want the client to be able to send me a JSON with this correct format
{"article":[{"code":"AAA","name":"car","price" : "10400.90", "sellers" : [{"seller1" : "Automaniac", "seller2" : "Autopro" }]}]}
Now I know how to parse it with TJSONObject.ParseJSONValue() by reading this JSON RadStudio
EDIT
If this JSON string is hardcoded it works fine with ParseJSONValue but if the string is taken from the URL my JSONstring only contains "]}" and obviously there is nothing to parse.
function TDM_Test.DS_insertArticles(pUrlJsonString: String): string;
// Hardcoded JSON string
jsonString := '{"article":[{"code":"AAA","name":"car","price" : "10400.90", "sellers" : [{"seller1" : "Automaniac", "seller2" : "Autopro" }]}]}';
JSonValue := TJSONObject.ParseJSONValue(jsonString);
// String received in url
jsonString := pUrlJsonString;
JSonValue := TJSONObject.ParseJSONValue(pUrlJsonString);
I found the way I wanted it to work by not using the string parameter of the function but instead using the Custom body by passing a JSONObject.
The method is POST and the content type is application/JSON.
function TDM_Test.DS_insertArticles(pUrlJsonObj: TJSONObject): string;
JSonValue := TJSONObject.ParseJSONValue(pUrlJsonObj);
// Retrieve data for database fields
artCode := JSONValue.GetValue<string>('article[0].code');
artName := JSONValue.GetValue<string>('article[0].name');
artPrice := JSONValue.GetValue<string>('article[0].price');
//... Proceed to INSERT SQL with these values

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

How to serialize JSON key containing dots (like e.g. IP address) with SuperObject?

I'm trying to save JSON where IP is a key. Expected JSON result is:
{"SnmpManagers":[{"10.112.25.235":162}]}
The Delphi SuperObject code:
const
IpAddr = '10.112.25.235';
Port = 162;
var
tmp: TSuperObject;
begin
tmp := TSuperObject.Create;
tmp.I[IpAddr] := Port;
Json.A['SnmpManagers'].Add(tmp);
end;
SuperObject parses dots as path delimiters of a JSON object:
{"SnmpManagers":[{"10":{"112":{"25":{"235":162}}}}]}
How to save IP as a JSON key correctly with SuperObject ?
The solution is to create JSON object from string
Json.A['SnmpManagers'].Add(SO(Format('{"%s":%d}', [IpAddr, Port])));
Another way to add (do not use with .O[] because AsObject gives nil for non existing keys):
// for a simple key-value object
Json.AsObject.S['1.2.3'] := 'a'; // gives us {{"1.2.3":"a"}}
Json.AsObject.S['4.5'] := 'b'; // gives us {{"1.2.3":"a"}, {"4.5":"b"}}
This also works:
var
tmp: ISuperObject;
begin
tmp := SO([IpAddr, port]);
Json.A['SnmpManagers'].Add(tmp);

Custom marshaling TDictionary in Delphi

I need to custom marshal/unmarchal a TDictionary in Delphi (XE). The dictionary is declared as:
TMyRecord = record
key11: integer;
key12: string;
...
end;
TMyDict: TDictionary<string, TMyRecord>;
Now, if i marshal the dictionary without registering a custom converter, the marshaller will put all kind of fields in the JSON string - FOnValueNotify, FKeyCollection, FItems, etc.
What i need is some sort of associative array of associative arrays, i.e.
{"key1":{"key11":"val1","key12":"val2"},"key2":{"key11":"val3","key12":"val4"}}
Unfortunately, i don't know how to write the custom converter and reverter. I'm using Delphi XE and the built in TJSONMarshal and TJSONUnMarshal.
Note: The use of TDictionary for this task is not required. I just cant come with something better.
For a simple case like yours, I tend to use a custom method to represent my object in JSON. But, if you want to create reverter and converter, you should read this article:
http://www.danieleteti.it/?p=146
Another option is TSuperObject which has the ability to marshal to/from JSON using RTTI:
type
TData = record
str: string;
int: Integer;
bool: Boolean;
flt: Double;
end;
var
ctx: TSuperRttiContext;
data: TData;
obj: ISuperObject;
begin
ctx := TSuperRttiContext.Create;
try
data := ctx.AsType<TData>(SO('{str: "foo", int: 123, bool: true, flt: 1.23}'));
obj := ctx.AsJson<TData>(data);
finally
ctx.Free;
end;
end;