Delphi Function to convert enum to string list....? - function

I'd like a function that takes an enum and returns a stringlist (or name:value pairs). It must be possible, the Object Inspector and Code Complete seem to do that. The pseudocode seems simple enough, but the details escape me....
function ParseEnum(e: Enum, S: TStringList): TStringList;
var
i: Integer;
begin
for i := 0 to length(enum) - 1 do
S.Add(GetEnumName(e, i));
Result := S;
end;

if this is a common funciton(all of Enum):
if the Enum is Continuity, as follows code is OK(by rtti)
if not Continuity i can't find a way now
type
TMyEnum = (s, b, c, d, e, f);
implementation
uses
System.Rtti, TypInfo;
procedure ParseEnum<T>(sl: TStrings);
var
rt: TRttiType;
rot: TRttiOrdinalType;
i: Integer;
begin
rt := TRttiContext.Create.GetType(TypeInfo(T));
rot := rt.AsOrdinal;
for i := rot.MinValue to rot.MaxValue do
sl.Add(GetEnumName(TypeInfo(T), i));
end;
procedure TForm1.btn1Click(Sender: TObject);
var
sl: TStringList;
begin
sl := TStringList.Create;
try
ParseEnum<TMyEnum>(sl);
ShowMessage(sl.Text);
finally
sl.Free;
end;
end;
why sl as a param but result: to attent people don't forget to free

Related

Marshal a Record to JSON and back [duplicate]

How can an array of record be stored in JSON via SuperObject library. For example..
type
TData = record
str: string;
int: Integer;
bool: Boolean;
flt: Double;
end;
var
DataArray: Array[0..100] of TData;
Just use the superobject Marshalling TSuperRTTIContext
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
superobject,
System.SysUtils;
type
TData = record
str : string;
int : Integer;
bool : Boolean;
flt : Double;
end;
TDataArray = Array [0 .. 100] of TData;
procedure Test;
var
DataArray : TDataArray;
so : ISuperObject;
ctx : TSuperRttiContext;
begin
ctx := TSuperRttiContext.Create;
try
so := ctx.AsJson<TDataArray>( DataArray );
finally
ctx.Free;
end;
Writeln( so.AsJson );
end;
begin
try
Test;
except
on E : Exception do
Writeln( E.ClassName, ': ', E.Message );
end;
ReadLn;
end.
Make it a string first.
Your array:
//Array[0] := 'Apple';
//Array[1] := 'Orange';
//Array[2] := 'Banana';
myArrayAsStr := '"MyArray": [{ "1": "' + Array[0] +'", "2": "' + Array[1] +'"}';
Then you can just make it into JSON with SO(myArrayAsStr)
You can always generate your array as string in a different procedure but I think thats the way to do it.
Ill keep checking if there is an easier way ;)
EDIT:
SuperObject also has the following function:
function SA(const Args: array of const): ISuperObject; overload;
You will be able to convert that to a string again and add it in the total JSON string.

D10 to DXE5: what's the equivalent of TJSONBool.Create(False) and JSONObj.ToJSON?

The folowing code compiles fine on D10 Seattle, but my pc where have D10 is installed is broken. Then i'm needing make a small update in my project using DXE5 but not compiles because the commands TJSONBool.Create(False) and JSONObj.ToJSON are present.
what's are equivalent of TJSONBool.Create(False) and JSONObj.ToJSON respectivally on DXE5?
uses
Data.DBXJSON, SHFolder;
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;
pathchrome: String;
JSONObj, ObjIpp: TJSONObject;
JSONPair: TJSONPair;
OldValue: string;
begin
specialFolder := CSIDL_LOCAL_APPDATA;
pathchrome := GetSpecialFolderPath(specialFolder);
pathchrome := pathchrome + '\Google\Chrome\User Data\Local State';
if fileexists(pathchrome) then
begin
JSONObj := TJSONObject.ParseJSONValue(TFile.ReadAllText(pathchrome)) as TJSONObject;
if not Assigned(JSONObj) then Exit; {raise Exception.Create('Cannot read file: ' + pathchrome);}
try
OldValue := JSONObj.GetValue<string>(ATarget);
if OldValue = '' then
Exit;
if not SameText(OldValue, Avalue) then
begin
JSONPair := JSONObj.Get(ATarget);
JSONPair.JsonValue.Free;
JSONPair.JsonValue := TJSONString.Create(Avalue);
ObjIpp := TJSONObject.Create;
ObjIpp.AddPair('enabled', TJSONBool.Create(False));
JSONObj.AddPair('hardware_acceleration_mode', ObjIpp);
TFile.WriteAllText(pathchrome, JSONObj.ToJSON);
end;
finally
JSONObj.Free;
end;
end;
end;
////////////////////// USAGE /////////////////////////
ChangeChromeSetting('hardware_acceleration_mode_previous', 'false');
TJSONBool and TJSONAncestor.AsJSON didn't exist yet in XE5. ToJSON was added in XE7, and TJSONBool was added in 10.0 Seattle.
In older versions, use TJSONTrue/TJSONFalse and TJSONObject.ToString instead:
ObjIpp.AddPair('enabled', TJSONFalse.Create);
...
TFile.WriteAllText(pathchrome, JSONObj.ToString);

Inno setup reading binary files [duplicate]

I want to open a 50 MB binary file and read only the last 4 bytes and convert it to string for some purpose.
The only way I found to do it now is using LoadStringFromFile, to load the file entirely on the memory then copy that last 4 bytes, however this method is very slow because the binary file is heavy.
Is there any better way to do it in Inno Setup script?
Update: This is a final working function that I edited from Martin Prikryl's answer
function readlast4byte() : AnsiString;
var
Stream: TFileStream;
Buffer: string;
Count: Integer;
Index: Integer;
begin
Count := 4;
Stream := TFileStream.Create('C:\test.txt', fmOpenRead);
try
Stream.Seek(-Count, soFromEnd);
SetLength(Buffer, 1);
SetLength(Result, Count);
for Index := 1 to Count do
begin
Stream.ReadBuffer(Buffer, 1);
Result[Index] := Chr(Ord(Buffer[1])) ;
end;
finally
Stream.Free;
end;
end;
Update 2: Also this is another great working function that written by TLama and should mark as answer too:
[Code]
#IFNDEF Unicode
#DEFINE CharSize 1
#ELSE
#DEFINE CharSize 2
#ENDIF
type
TSeekOrigin = (
soBeginning,
soCurrent,
soEnd
);
#IFDEF UNICODE
function BufferToAnsi(const Buffer: string): AnsiString;
var
W: Word;
I: Integer;
begin
SetLength(Result, Length(Buffer) * 2);
for I := 1 to Length(Buffer) do
begin
W := Ord(Buffer[I]);
Result[(I * 2)] := Chr(W shr 8); // high byte
Result[(I * 2) - 1] := Chr(Byte(W)); // low byte
end;
end;
#ENDIF
function ReadStringFromFile(
const FileName: string; Origin: TSeekOrigin; Offset, Length: Integer;
var S: AnsiString): Boolean;
var
Buffer: string;
Stream: TFileStream;
begin
Result := True;
try
Stream := TFileStream.Create(FileName, fmOpenRead);
try
Stream.Seek(Offset, Ord(Origin));
SetLength(Buffer, Length div {#CharSize});
Stream.ReadBuffer(Buffer, Length);
#IFNDEF UNICODE
S := Buffer;
#ELSE
S := BufferToAnsi(Buffer);
#ENDIF
finally
Stream.Free;
end;
except
Result := False;
end;
end;
You can use TFileStream support class:
TStream = class(TObject)
function Read(Buffer: String; Count: Longint): Longint;
function Write(Buffer: String; Count: Longint): Longint;
function Seek(Offset: Longint; Origin: Word): Longint;
procedure ReadBuffer(Buffer: String; Count: Longint);
procedure WriteBuffer(Buffer: String; Count: Longint);
function CopyFrom(Source: TStream; Count: Longint): Longint;
property Position: Longint; read write;
property Size: Longint; read write;
end;
THandleStream = class(TStream)
constructor Create(AHandle: Integer);
property Handle: Integer; read;
end;
TFileStream = class(THandleStream)
constructor Create(Filename: String; Mode: Word);
end;
Use the .Seek(-4, soFromEnd) property to seek the read pointer to the desired position.
A complication is that the TStream works with characters not bytes, so you have to convert Unicode strings that your read back to bytes.
When reading just four bytes, it's way easier to read byte by byte, preventing any multibyte conversions:
procedure ReadFileEnd;
var
Stream: TFileStream;
Buffer: string;
Count: Integer;
Index: Integer;
begin
Count := 4;
Stream := TFileStream.Create('my_binary_file.dat', fmOpenRead);
try
Stream.Seek(-Count, soFromEnd);
SetLength(Buffer, 1);
for Index := 1 to Count do
begin
Stream.ReadBuffer(Buffer, 1);
Log(Format('Byte %2.2x: %2.2x', [Index, Ord(Buffer[1])]));
end;
finally
Stream.Free;
end;
end;
Here's a generic alternative from by #TLama that efficiently works for arbitrary large read:
https://pastebin.com/nzGEdXVj
By the way, for me LoadStringFromFile function seems to be efficient enough to load 50 MB file. It takes only 40 ms.

Functions as return value in Pascal

I want to return a function as a return value in Pascal.
Generally it should look something like this:
function a(function b: Integer): function : Integer;
begin
a := b;
end;
but this doesn't work. I know that there is some problem with returning function as
a return value from another function but as far as I know this code should somehow work
what am I missing?
You need to define a function type to do what you want. See the following code example:
type
TFunc = function: Integer;
function a(b: TFunc): TFunc;
begin
a := b;
end;
function x: Integer;
begin
x := 11;
end;
begin
Writeln(a(#x));
end.

Datasnap RESTful JSON Result About?

I have a TDataset JSON helper function.
I want to result return the following:
{"result":[{"rid":"2","firstname":"veli","lastname":"deli"},{"rid":"1","firstname":"ismail","lastname":"kocacan"}]}
But.Does not work.The return the following:
{"result":[[{"rid":"2","firstname":"veli","lastname":"deli"},{"rid":"1","firstname":"ismail","lastname":"kocacan"}]]}
I want to remove extra [ character...
How to extract this data and return ?
{"rid":"2","firstname":"veli","lastname":"deli"},{"rid":"1","firstname":"ismail","lastname":"kocacan"}
My TDataset JSON Helper function :
TDatasetJSONHelper = class helper for TDataset
public
function ToJSONData: TJSONArray;
function ToJSONDataWrapper: TJSONValue;
end;
function TDatasetJSONHelper.ToJSONData: TJSONArray;
var
jso: TJSONObject;
jsa: TJSONArray;
jsp: TJsonPair;
J: Integer;
avalue: TJSONValue;
begin
Self.Close;
Self.Open;
jsa := TJSONArray.Create();
while not Self.Eof do
begin
jso := TJSONObject.Create();
for J := 0 to FieldCount - 1 do
jso.AddPair(TJsonPair.Create(Fields[J].DisplayName, Fields[J].Value));
jsa.AddElement(jso);
Self.Next;
end;
Self.Close;
Result := jsa;
end;
function TDatasetJSONHelper.ToJSONDataWrapper: TJSONValue;
var
aJSON: TJSONObject;
apair: TJsonPair;
avalue: TJSONValue;
begin
aJSON := TJSONObject.ParseJSONValue
(TEncoding.ASCII.GetBytes
('{"result":[[{"rid":"2","firstname":"veli","lastname":"deli"},{"rid":"1","firstname":"ismail","lastname":"kocacan"}]]}'),
0) as TJSONObject;
apair := aJSON.Get(0);
avalue := apair.JsonValue;
// ??? avalue := '{"rid":"2","firstname":"veli","lastname":"deli"},{"rid":"1","firstname":"ismail","lastname":"kocacan"}';
Result := avalue;
end;
function TServerMethods1.GetPersonList: TJSONArray;
begin
Result := dm.AdoQuery1.ToJSONData;
end;
function TServerMethods1.GetPersonList2: TJSONValue;
begin
Result := dm.AdoQuery1.ToJSONDataWrapper;
end;
How can I solve this problem ?
GetInvocationMetadata().ResponseCode := 200;
GetInvocationMetadata().ResponseContent := '{"rid":"2","firstname":"veli","lastname":"deli"},{"rid":"1","firstname":"ismail","lastname":"kocacan"}';
FYI, we added a more complete and faster function, in our Open Source repository.
It is part of our mORMot framework, but can be used as a stand-alone unit, not tied to other features.
See in SynVirtualDataSet.pas:
function DataSetToJSON(Data: TDataSet): RawUTF8
See this commit and the associated forum thread.