Mike Littlewood
asked on
Creating class objects from an xml document
I am interested to see if anyone knows of a generic solution to creating objects from an xml document.
I have a base object which all my objects inherit from, and currently they can all save themselves as xml with their class type as a node heading, ie TMyObject.
What I want is the ability to go backwards now and create the objects from the xml document, but obviously you cannot create an object directly just from a string. Anyone got an optional idea on a method or techinique I could use to do this?
I have a base object which all my objects inherit from, and currently they can all save themselves as xml with their class type as a node heading, ie TMyObject.
What I want is the ability to go backwards now and create the objects from the xml document, but obviously you cannot create an object directly just from a string. Anyone got an optional idea on a method or techinique I could use to do this?
why not use the SOAP api? save objects to xml and load them back. sure, the framework is too big for what you want, but it does what you want, it's there in delphi so use it.
creating objects from a string =
read string from a file
read object from string
or read form from string that's read from dfm file
i'm still wondering when Delphi will put the .dfm in XML format
read string from a file
read object from string
or read form from string that's read from dfm file
i'm still wondering when Delphi will put the .dfm in XML format
ASKER
I'm assuming Ciuly that you need to have the objects created to load them?
I want an xml file to be able to create the correct object it should be loading as well as loading it.
I want an xml file to be able to create the correct object it should be loading as well as loading it.
XML Data Binding can be used to generate a class from a xml file. You can create object by using NewFoo,LoadFoo methods provided by the class. It's quite easy to bind a object with xml. The limitation is performance. And you can not use your base object.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
actually geert, for this kind of approach you would use a solution like the one provided by the tpersitent delphi streaming mechanism: registerclass ;) (which will add teh class to a list).
>> I'm assuming Ciuly that you need to have the objects created to load them?
no. you never used soap? then look around here: http://delphi.about.com/od/webservices/Developing_Web_Services_with_Delphi.htm
>> I'm assuming Ciuly that you need to have the objects created to load them?
no. you never used soap? then look around here: http://delphi.about.com/od/webservices/Developing_Web_Services_with_Delphi.htm
ciuly,
i'm an "old" fashioned programmer :)
i'll go read your link too now ;)
i'm an "old" fashioned programmer :)
i'll go read your link too now ;)
I wrote my own framework for this (serialization) using RTTI. supports both xml and binary and is extendable to any format. I also made about 90% of it usable from FPC (free pascal). but it's for my own use only for now, as many other usefull stuff. I was planning to come up with some kind of package and sell it for a few bucks, but people don't seem to be interested. obviously there are free alternatives and that's probably the reason why.
what I want to say is that I almost wanted to make it available for free in this questions, but when I saw that it's only 125 points ... well, I changed my mind :D
so, long story short, would any of you guys pay say ... 5$ for something like this? or less? or more?
(getting a little off-topic, I know :P )
what I want to say is that I almost wanted to make it available for free in this questions, but when I saw that it's only 125 points ... well, I changed my mind :D
so, long story short, would any of you guys pay say ... 5$ for something like this? or less? or more?
(getting a little off-topic, I know :P )
ASKER
I'll keep this question open for now and let you know what road I go down.
Is it a native Delphi or Delphi .Net?
In .Net you can use the standard XmlSerializer on System.Xml.Serialization.
In .Net you can use the standard XmlSerializer on System.Xml.Serialization.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
On another tought, I like this solution more...
it's less dependent on TPersistent, but still, you have to register the classes...
it's less dependent on TPersistent, but still, you have to register the classes...
type
TMyObject = class;
TMyObjectClass = class of TMyObject;
TMyObjectClassArray = array of TMyObjectClass;
TMyObject = class
private
class var FClasses: TMyObjectClassArray;
public
class procedure RegisterClass(AClass: TMyObjectClass); static;
class function CreateByName(AClassName: string): TMyObject; static;
end;
TBook = class(TMyObject)
end;
TKeyboard = class(TMyObject)
end;
implementation
uses
SysUtils,
StrUtils;
{ TMyObject }
class function TMyObject.CreateByName(AClassName: string): TMyObject;
var
MyClass: TMyObjectClass;
begin
Result := nil;
for MyClass in FClasses do
begin
if SameText(AClassName, MyClass.ClassName) then
begin
Result := MyClass.Create;
Break;
end;
end;
end;
class procedure TMyObject.RegisterClass(AClass: TMyObjectClass);
begin
SetLength(FClasses, Length(FClasses)+1);
FClasses[Length(FClasses)-1] := AClass;
end;
initialization
TMyObject.RegisterClass(TBook);
TMyObject.RegisterClass(TKeyboard);
end.
did you try serializing that? you'll notice it doens't work. because in order to use serialization you must enable rtti fro that specific class and it's descendants. that is done by usiong the $M switch, which is off by default (RTTI gives some overhead). TPersistent has it already that's why people suggest extending from there.
ASKER
So would the first example by huferry be the right way to do it if it has to depend on TPersistent?
yes, it is one way to do it. but that code only handles the registration and creation, not loading. for that you will still need to use the stream's read/writecomponent methods (if going with delphi's component streaming mechanism) or RTTI if going for something xml, as you want it.
there are many sites that deal with delphi and rtti on the net, all you need to do is google :)
http://www.blong.com/Conferences/BorConUK98/DelphiRTTI/CB140.htm
http://delphi.about.com/od/oopindelphi/a/delphirtti.htm
just to name a couple.
there are many sites that deal with delphi and rtti on the net, all you need to do is google :)
http://www.blong.com/Conferences/BorConUK98/DelphiRTTI/CB140.htm
http://delphi.about.com/od/oopindelphi/a/delphirtti.htm
just to name a couple.
well, you don't have actually to depend on TPersistent. After you are able to create an instance of a descendant of TMyObject like.. in my example TBook, you can pass the XML to the object to be deserialize.
I include a rough example how to do this... I hope you'll get the idea...
I include a rough example how to do this... I hope you'll get the idea...
type
TMyObject = class
protected
procedure DeserializeData(AXMLNode: IXMLNode); virtual; abstract;
...
public
class function Deserialize(AXml: string): TMyObject;
end;
TBook = class(TMyObject)
private
FTitle: string;
protected
procedure DeserializeData(AXMLNode: IXMLNode); override;
..
end;
..
..
class function TMyObject.Deserialize(AXml: string): TMyObject;
var
MyClassName: string;
MyNode: IXmlNode;
begin
// get the class name some how
MyClassName := GetClassName(AXml);
// use the method of the previous example
Result := CreateByName(MyClassName);
if Assigned(Result) then
begin
// convert it to xml node somehow
MyNode := GetNode(AXml);
Result.DeserializeData(MyNode);
end;
end;
...
procedure TBook.DeserializeData(AXmlNode: IXmlNode);
begin
inherited;
// get the value somehow...
FTitle := GetPropertyValue(AXmlNode, 'Title');
end;
now there is a sample with a lot of somehow
i'm really intrested in seeing those somehows translated to code
...
i'm really intrested in seeing those somehows translated to code
...
>> i'm really intrested in seeing those somehows translated to code
that's what RTTI is for, in case I wasn't so obvious the last time.
that's what RTTI is for, in case I wasn't so obvious the last time.
here comes the some hows...
Unzip the attached file.. rename .jpg into .zip.
Unzip again the file, then you'll have the complete program.
Unzip the attached file.. rename .jpg into .zip.
Unzip again the file, then you'll have the complete program.
unit uMyClass;
interface
uses
XmlDoc,
XmlIntf;
type
TXmlProperty = class
private
FNode: IXMLNode;
public
constructor Create(ANode: IXmlNode);
function GetClassName: string;
function GetChildValue(AChildName: string): string;
end;
TMyClass = class;
TMyClassClass = class of TMyClass;
TMyClassClassArray = array of TMyClassClass;
TMyClass = class
private
class var FClasses: TMyClassClassArray;
class function CreateByName(AClassName: string): TMyClass; static;
protected
procedure DeserializeData(AXmlProp: TXmlProperty); virtual; abstract;
public
class procedure RegisterClass(AClass: TMyClassClass); static;
class function Deserialize(ANode: IXMLNode): TMyClass; overload; static;
class function Deserialize(AXml: string): TMyClass; overload; static;
end;
TBook = class(TMyClass)
private
FAuthor: string;
FTitle: string;
protected
procedure DeserializeData(AXmlProp: TXmlProperty); override;
public
property Title: string read FTitle;
property Author: string read FAuthor;
end;
implementation
{ TMyClass }
class function TMyClass.CreateByName(AClassName: string): TMyClass;
var
MyClass: TMyClassClass;
begin
Result := nil;
for MyClass in FClasses do
begin
if MyClass.ClassName = AClassName then
begin
Result := MyClass.Create;
Break;
end;
end;
end;
class function TMyClass.Deserialize(ANode: IXMLNode): TMyClass;
var
Prop: TXmlProperty;
begin
Result := nil;
Prop := TXmlProperty.Create(ANode);
try
Result := CreateByName(Prop.GetClassName);
if Assigned(Result) then
Result.DeserializeData(Prop);
finally
Prop.Free;
end;
end;
class function TMyClass.Deserialize(AXml: string): TMyClass;
var
XmlDoc: IXMLDocument;
begin
Result := nil;
XmlDoc := TXMLDocument.Create(nil);
XmlDoc.XML.Text := AXml;
XmlDoc.Active := True;
Result := Deserialize(XmlDoc.ChildNodes.First);
end;
class procedure TMyClass.RegisterClass(AClass: TMyClassClass);
begin
SetLength(FClasses, Length(FClasses)+1);
FClasses[Length(FClasses)-1] := AClass;
end;
{ TXmlProperty }
constructor TXmlProperty.Create(ANode: IXmlNode);
begin
inherited Create;
FNode := ANode;
end;
function TXmlProperty.GetChildValue(AChildName: string): string;
begin
Result := FNode.ChildValues[AChildName];
end;
function TXmlProperty.GetClassName: string;
begin
Result := FNode.NodeName;
end;
{ TBook }
procedure TBook.DeserializeData(AXmlProp: TXmlProperty);
begin
FAuthor := AXmlProp.GetChildValue('Author');
FTitle := AXmlProp.GetChildValue('Title');
end;
initialization
TMyClass.RegisterClass(TBook);
end.
xml-serial.zip
RTTI works only with published read/write properties. Sometime you also want to serialize some private members or read-only properties.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
I found this:
http://www.simdesign.nl/xml.html
you can store,read and create any TPresistent to/from XML.
The component is not free though.
http://www.simdesign.nl/xml.html
you can store,read and create any TPresistent to/from XML.
The component is not free though.
ASKER
Sorry been a while since I saw this thread, forgot.
Going to spend a little time reading what has been written.
And sorry huferry, I should have said it was standard delphi not .net
Going to spend a little time reading what has been written.
And sorry huferry, I should have said it was standard delphi not .net
ASKER
This got me started.