i'm working with gmlib (1.5.3, XE10) and tried to use TGeometry.interpolate function but always returns 0.00,0.00. I browse the code an I note that the function interpolate at GMFunctions.pas is empty??
Regards
wow! So sorry!!
Try with this code! (not tested)
class procedure TGeometry.Interpolate(Map: TCustomGMMap; Origin, Dest: TLatLng;
Fraction: Real; Result: TLatLng);
const
StrParams = '%s,%s,%s,%s,%s';
var
Params: string;
begin
if not Assigned(Result) then Exit;
Params := Format(StrParams, [
Origin.LatToStr(Map.Precision),
Origin.LngToStr(Map.Precision),
Dest.LatToStr(Map.Precision),
Dest.LngToStr(Map.Precision),
StringReplace(FloatToStr(Fraction), ',', '.', [rfReplaceAll])
]);
THackMap(Map).ExecuteScript('Interpolate', Params);
Result.Lat := Result.StringToReal(THackMap(Map).FWC.GetStringField(GeometryForm, GeometryFormInterLat));
Result.Lng := Result.StringToReal(THackMap(Map).FWC.GetStringField(GeometryForm, GeometryFormInterLng));
end;
Please give me a feedback. Thanks!
Related
I'm trying to convert the result of my Sqlite query into a Json,
to use the same procedures I use with remote binding to Sql Server by php.
The code works, but do you think it's a better solution?
Anyone there do that?
function TLogin.RetornaRegistros(query:String): String;
var
FDQuery : TFDQuery;
field_name,nomeDaColuna,valorDaColuna : String;
I: Integer;
begin
FDQuery := TFDQuery.Create(nil);
try
FDQuery.Connection := FDConnection1;
FDQuery.SQL.Text := query;
FDQuery.Active := True;
FDQuery.First;
result := '[';
while (not FDQuery.EOF) do
begin
result := result+'{';
for I := 0 to FDQuery.FieldDefs.Count-1 do
begin
nomeDaColuna := FDQuery.FieldDefs[I].Name;
valorDaColuna := FDQuery.FieldByName(nomeDaColuna).AsString;
result := result+'"'+nomeDaColuna+'":"'+valorDaColuna+'",';
end;
Delete(result, Length(Result), 1);
result := result+'},';
FDQuery.Next;
end;
FDQuery.Refresh;
Delete(result, Length(Result), 1);
result := result+']';
finally
FDQuery.Free;
end;
end;
That is not a good approach. I really suggest consider at least three options:
Use the power of System.JSON unit.
Uses {...} System.JSON;
Var
FDQuery : TFDQuery;
field_name,Columnname,ColumnValue : String;
I: Integer;
LJSONObject:TJsonObject;
begin
FDQuery := TFDQuery.Create(nil);
try
FDQuery.Connection := FDConnection1;
FDQuery.SQL.Text := query;
FDQuery.Active := True;
FdQuery.BeginBatch;//Don't update external references until EndBatch;
FDQuery.First;
LJSONObject:= TJSONObject.Create;
while (not FDQuery.EOF) do
begin
for I := 0 to FDQuery.FieldDefs.Count-1 do
begin
ColumnName := FDQuery.FieldDefs[I].Name;
ColumnValue := FDQuery.FieldByName(ColumnName).AsString;
LJSONObject.AddPair(TJSONPair.Create(TJSONString.Create( ColumnName),TJSONString.Create(ColumnValue)));
FDQuery.Next;
end;
//FDQuery.Refresh; that's wrong
FdQuery.EndBatch;
finally
FDQuery.Free;
Showmessage(LJSonObject.ToString);
end;
end;
https://www.youtube.com/watch?v=MLoeLpII9IE&t=715s
Second approach, use FDMemTable.SaveToStream;
The same works for FDMemTable.SaveToFile;
Put a TFDMemTable on Datamodule (Or form, as well).
fMStream:TMemoryStream;
Begin
FDQuery := TFDQuery.Create(nil);
try
FDQuery.Connection := FDConnection1;
FDQuery.SQL.Text := query;
FDQuery.Active := True;
//fdMemTable1.Data:=fdQuery.Data; {note *2}
fdMemTable1.CloneCursor(FdQuery,true,true);{note *3}
fMStream:=TMemoryStream.Create;
FdMemTable1.SaveToStream(fMStream,sfJson);
finally
FDQuery.Free;
FdMemTable.Close;
end;
Now you can Read the JSON content
For example, following answer Converting TMemoryStream to 'String' in Delphi 2009
function MemoryStreamToString(M: TMemoryStream): string;
begin
SetString(Result, PChar(M.Memory), M.Size div SizeOf(Char));
end;
and you have the json as String
The BatchMove suggeted by #VictoriaMarotoSilva
You can use BatchMove components, which provides an interface to move data between datasets, but it works better for backup and importation when you want to save data in drive, XML or json format. I didn't find examples yet, using data moving in memory; if somebody else has an example, please comment.
Notes
Using FdMemTable, don't forget drag TFDStanStorageJSONLink component for datamodule
method .Data just works for FiredacDatasets (Datasets with prefix FD).
To assign data for memTable in old Datasets use method .Copydata instead.
Sorry guys, I change .Data to .CloneCursor to share the same Memory Space with both datasets.
I just modified my first answer below to comport different type of field to convert number, date and boolean in appropriate json format.
I comment the Types I didn't test.
Look
Uses {...} System.JSON;
Var
FDQuery : TFDQuery;
field_name, Columnname, ColumnValue : String;
I: Integer;
LJSONObject:TJsonObject;
begin
FDQuery := TFDQuery.Create(nil);
try
FDQuery.Connection := FDConnection1;
FDQuery.SQL.Text := query;
FDQuery.Active := True;
FdQuery.BeginBatch;//Don't update external references until EndBatch;
FDQuery.First;
LJSONObject:= TJSONObject.Create;
while (not FDQuery.EOF) do
begin
for I := 0 to FDQuery.FieldDefs.Count-1 do
begin
ColumnName := FDQuery.FieldDefs[I].Name;
Case FDQuery.FieldDefs[I].Datatype of
ftBoolean:
IF FDQuery.FieldDefs[I].Value=True then LJSONObject.AddPair(TJSONPair.Create(TJSONString.Create( ColumnName),TJSONTrue.Create)) else
LJSONObject.AddPair(TJSONPair.Create(TJSONString.Create( ColumnName),TJSONFalse.Create));
ftInteger,ftFloat{,ftSmallint,ftWord,ftCurrency} :
LJSONObject.AddPair(TJSONPair.Create(TJSONString.Create( ColumnName),TJSONNumber.Create(FDQuery.FieldDefs[I].value)));
ftDate,ftDatetime,ftTime:
LJSONObject.AddPair(TJSONPair.Create(TJSONString.Create( ColumnName),TJSONString.Create(FDQuery.FieldDefs[I].AsString)));
//or TJSONString.Create(formatDateTime('dd/mm/yyyy',FDQuery.FieldDefs[I].Value));
else LJSONObject.AddPair(TJSONPair.Create(TJSONString.Create( ColumnName),TJSONString.Create(FDQuery.FieldDefs[I].AsString)));
End;
FDQuery.Next;
end;
FdQuery.EndBatch;
finally
FDQuery.Free;
Showmessage(LJSonObject.ToString);
end;
end;
More about dataset.DataType http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/DB_TFieldType.html
More about JSONTypes https://community.embarcadero.com/blogs/entry/json-types-for-server-methods-in-datasnap-2010-4
Consider using TFDBatchMove component. It's for direct transferring of data between two databases with additional mappings support. As a source and target can be a text, dataset or an SQL query to any of the FireDAC's supported database engines.
The Delphi MVC Framework contains a powerful mapper to map json to objects and datasets to objects. The Mapper is a sub project. It's independent code that can also be used in other kind of projects. It is open-source!
Advantage is that boolean values for example, are converted to TJSONBool type and not a string. I suggest take a look at the samples.
https://github.com/danieleteti/delphimvcframework/tree/master/samples/objectsmapperssamples
Probably not the best solution, and you can modify this to how you would like the JSON to be formatted... here is a quick sample solution:
function GetDataSetAsJSON(DataSet: TDataSet): TJSONObject;
var
f: TField;
o: TJSOnObject;
a: TJSONArray;
begin
a := TJSONArray.Create;
DataSet.Active := True;
DataSet.First;
while not DataSet.EOF do begin
o := TJSOnObject.Create;
for f in DataSet.Fields do
o.AddPair(f.FieldName, VarToStr(f.Value));
a.AddElement(o);
DataSet.Next;
end;
DataSet.Active := False;
Result := TJSONObject.Create;
Result.AddPair(DataSet.Name, a);
end;
I have this problem.
Im at the End of this Function:
FUNCTION ToString(Liste : Pokemon) : String;
VAR
RES : STRING;
BEGIN
ClrScr;
TextBackground(Green);
Writeln('DER POKEDEX:');
Writeln;
WHILE (Liste <> NIL) DO
BEGIN
RES := RES + Concat('#',IntToStr(Liste^.PkmnPos), ': ', Liste^.PkmnName, '. // ', IntToStr(Liste^.PkmnKG), ' kg', chr(13),chr(10),chr(13),chr(10));
Liste := Liste^.Next;
END;
TextBackground(Black);
ToString := Res;
END;
Now, I have the Procedure called "Submenu". So, when in the Main Program code, i can just call the procedure "Submenu()". But when im within this above functions, it wont compile my code. It says "identifier not found". But, after im done with this function the last thing it needs to do is go into submenu. And im really trying not to builed an infinite loop in wihtin the main program only to not have the programm end. What is the best way of doing that?
O, and I know, that if I have the function Submenu started before the other functions, it would work. But both functions call each other, so neither can be on top of each other because there will always be one, that wont work...
Regards.
Then you need a forward declaration:
FUNCTION ToString(Liste : Pokemon) : String; FORWARD;
FUNCTION Submenu();
BEGIN
..
ToString(Liste);
..
END;
FUNCTION ToString(Liste : Pokemon) : String;
BEGIN
// real implementation tostring
..
Submenu();
..
END;
Note the declaration with FORWARD
I have a code that changes a value of a determinated pair in a existent JSON file and works perfectly. Now i need add one object and one pair to this file, using great part this same code. So how do this?
Thank you.
uses
System.Json, ShFolder, System.IOUtils;
...
function GetSpecialFolderPath(folder : integer) : string;
const
SHGFP_TYPE_CURRENT = 0;
var
path: array [0..MAX_PATH] of char;
begin
if SUCCEEDED(SHGetFolderPath(0,folder,0,SHGFP_TYPE_CURRENT,#path[0])) then
Result := path
else
Result := '';
end;
procedure ChangeChromeSetting(const ATarget, Avalue: string);
var
specialfolder: integer;
caminhochrome: String;
JSONObj, Obj: TJSONObject;
JSONPair: TJSONPair;
OldValue: string;
begin
specialFolder := CSIDL_LOCAL_APPDATA;
caminhochrome := GetSpecialFolderPath(specialFolder);
caminhochrome := caminhochrome + '\Google\Chrome\User Data\Local State';
if fileexists(caminhochrome) then
begin
Obj := TJSONObject.Create;
JSONObj := TJSONObject.ParseJSONValue(TFile.ReadAllText(caminhochrome)) as TJSONObject;
if not Assigned(JSONObj) then raise Exception.Create('Cannot read file: ' + caminhochrome);
try
OldValue := JSONObj.GetValue<string>(ATarget);
if not SameText(OldValue, Avalue) then
begin
JSONPair := JSONObj.Get(ATarget);
JSONPair.JsonValue.Free;
JSONPair.JsonValue := TJSONString.Create(Avalue);
///////////////////////////////////////////////////
Obj.AddPair('enabled', TJSONBool.Create(false)); // Trying add pair
JSONObj.AddPair('hardware_acceleration_mode', Obj); // Trying add object
//////////////////////////////////////////////////
TFile.WriteAllText(caminhochrome, JSONObj.ToJSON); // Don't add object and pair
end;
finally
JSONObj.Free;
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ChangeChromeSetting('hardware_acceleration_mode_previous', 'false');
end;
This is result that i'm waiting
"hardware_acceleration_mode":{"enabled":false}
Your code is somewhat confusing since you pass in some of the names as arguments, but then hard code others inside the function. Abstracting functionality is good practise but before you can abstract you really need to ensure the code works correctly. I'm going to show code that does not attempt to be abstract. Once you are satisfied it behaves as you need, then feel free to abstract away.
This code does what I believe is your intent:
var
root: TJSONObject;
value: TJSONObject;
prev: string;
begin
root := TJSONObject.ParseJSONValue(TFile.ReadAllText(FileName)) as TJSONObject;
try
prev := root.GetValue<string>('hardware_acceleration_mode_previous');
if not SameText(prev, 'false') then
begin
// remove existing value, if it exists
root.RemovePair('hardware_acceleration_mode').Free;
// create a new object, and initialise it
value := TJSONObject.Create;
value.AddPair('enabled', 'false');
// add the object at the root level
root.AddPair('hardware_acceleration_mode', value);
// save to file
TFile.WriteAllText(FileName, root.ToJSON);
end;
finally
root.Free;
end;
end;
Note that I have ensured that there are no memory leaks. I've used RemovePair to make sure that if there is an existing value named hardware_acceleration_mode it is first removed.
I'm using FireDAC in Delphi XE6 to query a database (Pervasive) through ODBC. I have a TFDQuery component which runs my SELECT query and returns the records. Once the query is complete I want to export the data in the recordset as JSON. I've tried using the following code :
fdacQuery.SaveToStream(myStream, sfJSON);
This creates JSON, but only for the table definition i.e. field names, data types, constraints etc. - there is no representation of the data. Is there another method I should be using to export just the recordset data as JSON? Is there another solution?
Try this on for size then. I did it for a utility I needed yesterday. It uses SuperObject. I left all field types in the code in case you want to add other special treatments or tweak any of those I put in. It's working for me on many random datasets right now.
class procedure TTool.ExportDataSetToJson(DataSet: TDataSet; FileName: string; Append: boolean = false);
const
SData = 'data';
var
json : ISuperObject;
item : ISuperObject;
wasActive: boolean;
fld : TField;
begin
json := SO;
json.O[SData] := SA([]);
wasActive := DataSet.Active;
try
DataSet.Active := true;
DataSet.First;
while not DataSet.Eof do
begin
item := SO;
for fld in DataSet.Fields do
begin
case fld.DataType of
// ftUnknown: ;
ftString,
ftBlob,
ftMemo,
ftFmtMemo,
ftBytes,
ftVarBytes,
ftFixedChar,
ftFixedWideChar,
ftWideMemo,
ftByte,
ftWideString: item.S[fld.FieldName] := fld.AsString;
ftBoolean: item.B[fld.FieldName] := fld.AsBoolean;
ftFloat,
ftSingle,
ftExtended,
ftCurrency,
ftFMTBcd,
ftBCD: item.D[fld.FieldName] := fld.AsFloat;
ftTime : item.S[fld.FieldName] := TimeToJson(fld.AsDateTime);
ftDate,
ftTimeStamp,
ftOraTimeStamp,
ftDateTime: item.S[fld.FieldName] := DateTimeToJson(fld.AsDateTime);
ftSmallint,
ftInteger,
ftWord,
ftAutoInc,
ftLongWord,
ftShortint,
ftLargeInt: item.I[fld.FieldName] := fld.AsLargeInt;
// ftGraphic: ;
// ftParadoxOle: ;
// ftDBaseOle: ;
// ftTypedBinary: ;
// ftCursor: ;
// ftADT: ;
// ftArray: ;
// ftReference: ;
// ftDataSet: ;
// ftOraBlob: ;
// ftOraClob: ;
// ftVariant: ;
// ftInterface: ;
// ftIDispatch: ;
ftGuid: item.S[fld.FieldName] := fld.AsString;
// ftOraInterval: ;
// ftConnection: ;
// ftParams: ;
// ftStream: ;
// ftTimeStampOffset: ;
// ftObject: ;
else
item.S[fld.FieldName] := fld.AsString;
end;
end;
DataSet.Next;
json.A[SData].Add(item);
end;
if Append then
TFile.AppendAllText(FileName, json.AsJSon(true, true))
else
json.SaveTo(FileName, true, true);
finally
DataSet.Active := wasActive;
end;
end;
Have you looked at the code in the tutorial at http://docwiki.embarcadero.com/RADStudio/XE8/en/Tutorial:_Using_a_REST_DataSnap_Server_with_an_Application_and_FireDAC yet?
// Create dataset list
Result := TFDJSONDataSets.Create;
// Add departments dataset
TFDJSONDataSetsWriter.ListAdd(Result, sDepartment, FDQueryDepartment);
// Add employees dataset
TFDJSONDataSetsWriter.ListAdd(Result, sEmployees, FDQueryDepartmentEmployees);
I am really new to Delphi and I am doing an experiment on how to output JSON array through delphi. This maybe sound simple to anyone but I just dont know how. I already created a simple program.
Now, what i want to do is to create a command/request with parameter like:
http://localhost:8001/hello?json={"names":["Jay","Chris","John"]}
that would create a result in the browser like this:
{
result: ["Hello Jay","Hello Chris","Hello John"],
id: "",
time_elapsed: 0
}
Please, i really need help on this. Anybody?
EDIT:
This is the code i just did today but it still doesn't show my desired output:
procedure TPrimeJSONMHelloPeople.ProcessJSONRPCRequest(
var ResultValue: TlkJSONbase; var ResultSuccess: Boolean);
var
jsonPeople:TlkJSONlist;
dmPool:TObject;
dm:TPrimeDataModuleBaseDM;
i:integer;
begin
FjsonObj1 := TlkJSONobject.Create;
jsonPeople := FjsonObj1.CreateListValue('names');
jsonPeople.AddVarString('jay');
jsonPeople.AddVarString('ann');
jsonPeople.AddVarString('john');
inherited;
CheckRequiredParameter('names');
PrimeDataModuleWebService.TDataModuleDMCreateInstanceDefault(dmPool);
try
dm := TPrimeDataModuleDefaultDM(dmPool).GetModule;
try
//this part here will loop and output the name
//if jsonPeople <> nil then
if Params.Field['names'] <> nil then
begin
for i := 0 to FjsonObj1.Field['names'].Count - 1 do
begin
ResultValue := TlkJSONlist.Create
end;
end;
ResultValue := TlkJSONlist.Create;
finally
dm.Release;
end;
finally
dmPool.Free;
end;
FjsonObj1.Free;
ResultSuccess := True;
end;
I don't know what's missing in the code, It only shows:
{
result: [ ],
id: "",
time_elapsed: 0
}
and not :
{
result: ["Hello Jay","Hello Chris","Hello John"],
id: "",
time_elapsed: 0
}
i have just found the right answer. Here's the code:
procedure TSample1.ProcessJSONRPCRequest(
var ResultValue: TlkJSONbase; var ResultSuccess: Boolean);
var
dmPool:TObject;
dm:TPrimeDataModuleBaseDM;
jsonPeople:TlkJSONlist; //used Tlkjsonlist since I want to create an array
i:integer;
begin
inherited;
jsonPeople:=TlkJSONlist.Create; //create jsonPeople as an array
CheckRequiredParameter('names'); //names parameter needed
PrimeDataModuleWebService.TDataModuleDMCreateInstanceDefault(dmPool);
try
dm := TPrimeDataModuleDefaultDM(dmPool).GetModule;
try
if Params.Field['names'] <> nil then //check if the names parameter is empty
begin
ResultValue:=jsonPeople;
for i := 0 to Params.Field['names'].Count - 1 do
begin
jsonPeople.AddVarString('hello ' + Params.Field['names'].Child[i].value);
end;
end;
finally
dm.Release;
end;
finally
dmPool.Free;
end;
ResultSuccess := True;
end;
end.
The request is http://localhost/sample1?json={"names":["john","jay"]}
The output is
{
-
result: [
"hello john"
"hello jay"
]
id: ""
time_elapsed: 0
}
Hope this can help someone who is new in creating web service request using delphi.
First of all, I think your URI shown in your question is already decoded. You should encode the URI parameters in the HTTP protocol.
If you want to create such HTTP-oriented JSON access, take a look at the RESTful approach. It would help you not reinvent the well, and be able to make your server more AJAX ready.
Then you seems to use the third-party lkJSON Delphi library... So you could get directly help from its author or support forum.
From the source code of the library, you should use a TlkJSONlist instance to handle a JSON array, from both URI input and result output.