lets says i have a code like this,
json:= TlkJSON.ParseText(Memo1.Text);
if Assigned(json) then
begin
json:= json.Field['journals'];
if Assigned(json) then
begin
json:= json.Field['journal_details'];
if Assigned(json) then
begin
Memo2.Lines.Add(VarToStr(json.Child[0].Value));
end;
end;
end;
it's used for tracing my json object had field "journals" and "journal_details" in it.
I want to make it more dynamic so it can detect if the value in json object was array/list/custom list and it contain all the detail error message i want to re - translate again so the user can understand the message.
Any clue will be appreciated.
as #MyICQ suggestion, this is snippet of data that i want to re - translate look like.
[{"code":"H003","validations":["phone_invalid_format"]},{"code":"H004","validations":["phone_invalid_format"]},{"code":"H005","validations":["phone_invalid_format"]},{"code":"H006","validations":["phone_invalid_format"]},{"code":"H010","validations":["phone_invalid_format"]}]
with note, the string was not always with the same structure.
this is the output, i wish to have at least
Error List Data :
> H003 : Note = Phone_invalid_format
> H004 : Note = Phone_invalid_format
> H005 : Note = Phone_invalid_format
> H006 : Note = Phone_invalid_format
> H010 : Note = Phone_invalid_format
so far all i can get is to detect how many array or object in the string, but failed in retrieve the value in it.
There is a function in lkJSON to find the type of element. This is called SelfType, enumeration of TlkJSONtypes: (jsBase, jsNumber, jsString, jsBoolean, jsNull, jsList, jsObject).
You can write this out using SelfTypeName where you get the same as enum, only as a string.
So basically something along:
begin
js := TlkJSON.ParseText(txt);
writeln(js.SelfTypeName); // get the type as a string
if js.SelfType in [jsList] then
begin
// process as list
end
else
if js.SelfType in [jsObject] then
begin
// process as object
end
else if js.SelfType not in [jsBase] then
begin
// write the value of number, string, boolean, null
end;
end;
Obviously you can then make a parse_element function which you can call recursively.
lkJSON seems to be still updated, this GitHub project lists 1.09 from 1 Oct 2021.
The documentation is fairly sparse, but it does the job on older Delphi versions where superobject unfortunately won't compile.
If you google keywords like SelfName and lkJSON there are plenty examples online to get you going.
Related
I've just started learning Ada and I cannot figure out how to keep the program running when the user input is beyond the declared range of a variable. I'd like to print info about bad range of input and then ask user for input again.
This is my simple code:
with Ada.Text_IO;
use Ada.Text_IO;
procedure Main is
type Score is range 0..100;
a : Score;
begin
Put_Line ("Enter value range 0-100: ");
a := Score'Value(Get_Line);
if a'Valid then
Put_Line ("You entered" & Score'Image (a));
else
Put_Line ("Bad range of input");
end if;
end Main;
Shouldn't I use the "range" in order to check the input, but rather some if's with >, < restrictions?
My other approach was to try this with exceptions, but it also doesn't work as I want it to:
with Ada.Text_IO;
with Ada.IO_Exceptions;
use Ada.Text_IO;
procedure Main is
type Score is range 0..100;
a : Score;
begin
loop
begin
Put_Line ("Enter value range 0-100: ");
a := Score'Value(Get_Line);
Put_Line ("You entered" & Score'Image (a));
exit;
exception
when Ada.IO_Exceptions.Data_Error =>
Put_Line("Bad range of input");
end;
end loop;
end Main;
I believe the problem is in my lack of understanding this language, but I hope there is some kind of easy solution for this, thanks for any help.
Now you know a magical incantation that works, but I doubt you understand why it works, or why your other incantations didn't work. I will go into painful pedagogical detail about that, in hopes that some of it might be useful or of interest.
In Ada, when you declare a type, the type is anonymous, and the name (Score) you give is the name of the first-named subtype. The first-named subtype may have constraints that don't apply to the anonymous base type. For some types, including integer types, it's possible to refer to the anonymous base type with 'Base.
Since you declared Score using range, it is a signed integer type and its base type is (roughly) symmetrical around zero. So your declaration is equivalent to something like
type Score'Base is range -128 .. 127;
subtype Score is Score'Base range 0 .. 100;
(this is not Ada and will not compile).
Score'Value returns a value of Score'Base (ARM 3.5 (53)), so if you input "101" or "-3", Score'Value will succeed and return the appropriate value. When you assign that value to your variable of subtype Score, a check is performed that the value is in the range of Score; when that fails, Constraint_Error is raised. If you input an invalid image, such as "200" or "xyz", Score'Value fails and raises Constraint_Error. So you have two kinds of incorrect input resulting in two different failures, both of which happen to raise the same exception.
Your first version failed because you never got to the if statement. Your second version failed because Ada.Text_IO.Get_Line never raises Data_Error.
When dealing with numeric input, I advise that a complete line be read into a String and you then parse out the value(s) from that String, as you have done. However, 'Value will reject some input that you might want to consider valid. For example, you might want to accept "23 skidoo" and get the value 23 from it. For that, you might want to instantiate Ada.Text_IO.Integer_IO for your numeric (sub)type and use the Get function that takes a String parameter:
package Score_IO is new Ada.Text_IO.Integer_IO (Num => Score);
...
Score_IO.Get (From => "23 skidoo", Item => A, Last => Last);
will set A to 23 and Last to the index of '3' in From (2).
HTH
I'm making a unit library in pascal at the moment. I would like to know whether I could assign a very long string without using Ansistring or {$H+}, because I couldn't assign typed files with reference-counted types. Thanks before.
EDIT : Code added
type
dataFilm = record
title : array [1..255] of char;
genre : array [1..255] of char;
viewerRating : array [1..255] of char;
duration : integer;
synopsis : array [1..2000] of char;
priceWeekdays : longint;
priceWeekend : longint;
end;
theatreUDT = record
mainDataFilm : array [0..100] of dataFilm;
end;
ftheatreUDT = record
fmainDataFilm : file of dataFilm; //It fails here when i use ansistring
end;
procedure loadMain(var main : theatreUDT; var fmain:ftheatreUDT);
begin
assign(fmain.fmainDataFilm,'Data/dataFilm.dat');
reset(fmain.fmainDataFilm);
end;
The program has no problem at all, only, it can't hold a very long string for synopsis.
You can use array of char to have longer strings.
dataFilm = record
title : string;
genre : string;
viewerRating : string;
duration : integer;
synopsis : array[0..1999] of char;
priceWeekdays : longint;
priceWeekend : longint;
end;
However, note that this is not a very good solution because
a) it means that you have to choose a maximum length for the string. If you choose the length too small, some texts will not fit. If you choose the length too big, you waste lots of disk space because the space a record in the file takes is always the same based on the definition of the record.
b) you cannot easily modify the structure of the data. For example, if you want to add a "director" field to the information about the movie, you cannot do so without writing code to convert the old file format to the new file format. Basically you cannot change the definition of the dataFilm type once you have created files with this code.
c) You cannot easily look into the files any other way. If you use some standard like JSON or XML or even a database, you can easily look at the content with other tools.
d) you cannot access the files with other programming environments as easily. You might want to migrate from Pascal to some other environment in the future or allow other tools to access the files. This is easier using some standard format or a database than when using the pascal way.
As the title suggests, using Delphi 2010 and MyDAC 7.1, how do I output an entire string as a string like JSON / XML / CSV or some other plain text option?
eg output:
{user_id:1;username:testuser;password:testpass}
Presuming that MyDAC is a standard TDataSet descendant, you can build the string manually. For instance, for JSON:
var
i: Integer;
Output: string;
begin
Output := '{'; // #123;
for i := 0 to MyQry.FieldCount - 1 do
Output := Output +
MyQry.Fields[i].FieldName + ':' + // #58
MyQry.Fields[i].AsString + ';'; // #59
// Replace final ; with closing } instead
Output[Length(Output)] := '}'; // #125
end;
Or you can Google to find a Delphi JSON library (like SuperObject) and use it instead, as is done here.
For XML, use the same type loop with TXMLDocument. You can search for previous posts here tagged with Delphi to find examples.
For CSV, it can get complicated based on your data and the requirements. For instance, do you want or need a header row with field names? Does your data contain data that contains spaces or punctuation or CR/LFs? The easiest solution is to search for an already-existing library (via Google or Bing, not here) that exports to CSV.
According to the documentation you can use the SaveToXML procedures. should be something like this:
var
MyQuery: TMyQuery;
begin
try
MyQuery := TMyQuery.Create();
MyQuery.Connection := AConnection;
MyQuery.SQL.Text := ACommand;
MyQuery.Execute();
MyQuery.SaveToXML(<tstream or file>)
except
raise;
end;
end;
I'm writing long digit arythmetics. This is a function for adding to longint long binary digits. I need to output the sum inside the function, to debug it. How could I do it, without creating new variables?
function add(var s1,s2:bindata;shift:longint):bindata;
var l,i:longint;
o:boolean;
begin
writeln(s1.len,' - ',s2.len);
o:=false;
l:=max(s1.len,s2.len);
add.len:=0;
for i:=1 to l do begin
if o then Begin
if s1.data[i+shift] then Begin
if (s2.data[i]) then add.data[i+shift]:=true
Else add.data[i+shift]:=false;
End
else if s2.data[i] then add.data[i+shift]:=false
else Begin
add.data[i+shift]:=true;
o:=false;
End;
End
Else Begin
if s1.data[i+shift] then Begin
if s2.data[i] then
Begin
add.data[i+shift]:=false;
o:=true;
End
Else add.data[i+shift]:=true;
End
else if s2.data[i] then add.data[i+shift]:=true
else add.data[i+shift]:=false;
End;
output(add); //Can I output a variable?
end;
add.len:=l;
if o then Begin
inc(add.len);
add.data[add.len]:=true;
End;
end;
You are accumulating the result of the function within the function result variable, which is generally fine, but uses an outdated style, and leads to exactly the problem you're facing here. You're trying to report an intermediate value of the function result, and to do that, you're trying to reference the name of the function, add. When you do that, though, the compiler interprets it as an attempt to report the function itself, rather than the expected return value of this particular invocation of the function. You'll get the address of the function, if output is defined to accept function addresses; otherwise, you'll get a compiler error.
If your compiler offers a certain common language extension, then you should use the implicit Result variable to refer to the intermediate return value instead of continuing to refer to it by the function name. Since Result is declared implicitly, you wouldn't have to create any other variables. The compiler automatically recognizes Result and uses it as an alias for the function's return value. Simply find every place you write add within the function and replace it with Result. For example:
if o then begin
Inc(Result.len);
Result.data[Result.len] := True;
end;
Turbo Pascal, Free Pascal, GNU Pascal, and Delphi all support the implicit Result variable, but if you've managed to get stuck with a compiler that doesn't offer that extension, then you have no choice but to declare another variable. You could name it Result, and then implement your function with one additional line at the end, like so:
function add(var s1, s2: bindata; shift: longint): bindata;
var
l, i: longint;
o: boolean;
Result: bindata;
begin
{
Previous function body goes here, but with
`add` replaced by `Result`
}
{ Finally, append this line to copy Result into the
function's return value immediately before returning. }
add := Result;
end;
The version of Delphi is 7.
I'm sending a query to a MySQL database. What can be returned is either a set of data rows or simply an empty set. Nothing unusual. But I have no idea how to make a checking mechanism that will check whether it is a set of data or an empty set.
Here's some code:
var
Q: TADOQuery;
begin
Q := TADOQuery.Create(self);
Q.Connection := ADOConnection;
Q.SQL.Add('CALL get_shopping_cart_list()'); // Call stored procedure
Q.Open; // Send query and get some
// results back
// PSEUDOCODE
// IF get_shopping_cart_list() RETURNS A NON-EMPY SET THEN
// SHOW WHAT WE HAVE
// ELSE
// SHOW A MESSAGE THAT SAYS 'EMPTY SET'
Q.Free;
end;
Depending on Delphi version it can be either
if Q.IsEmpty then ...
or
if Q.BOF and Q.EOF then ...
You can also dive into Microsoft ADO. As long as there are no multiple statements in your query, Q.RecordSet.EOF and Q.RecordSet.BOF should do.
http://msdn.microsoft.com/library/windows/desktop/ms675787.aspx
http://www.w3schools.com/ado/ado_ref_recordset.asp
http://msdn.microsoft.com/library/windows/desktop/ms677539.aspx
Also please do not forget to guard memory management of errors.
Q := TADOQuery.Create;
try
.... do this or that ....
.... do this or that ....
.... do this or that ....
finally
Q.free;
end;