Componentes.Terceros.RemObj.../official/5.0.35.741/Data Abstract for Delphi/Source/uDAXMLUtils.pas
2009-02-27 15:16:56 +00:00

643 lines
21 KiB
ObjectPascal

unit uDAXMLUtils;
{----------------------------------------------------------------------------}
{ Data Abstract Library - Core Library }
{ }
{ compiler: Delphi 6 and up, Kylix 3 and up }
{ platform: Win32, Linux }
{ }
{ (c)opyright RemObjects Software. all rights reserved. }
{ }
{ Using this code requires a valid license of the Data Abstract }
{ which can be obtained at http://www.remobjects.com. }
{----------------------------------------------------------------------------}
{$I DataAbstract.inc}
interface
uses Classes, TypInfo, uROXMLIntf, uDAInterfaces;
const
// Misc
ValueNIL = 'NIL';
// Attribute names
attr_Type = 'Type';
attr_IsObject = 'IsObject';
attr_ByRef = 'ByRef';
attr_RefName = 'RefName';
attr_Owner = 'Owner';
attr_IsCollection = 'IsCollection';
attr_IsArray = 'IsArray';
attr_Count = 'Count';
attr_ItemClass = 'ItemClass';
attr_Item = 'Item';
attr_ClassName = 'ClassName';
attr_IsNull = 'IsNull';
type
{ Misc }
TROXMLStreamerOption = (soIncludeType);
TROXMLStreamerOptions = set of TROXMLStreamerOption;
{ TROXMLStreamer }
TROXMLStreamer = class(TComponent)
private
fXMLStreamerOptions: TROXMLStreamerOptions;
fPropertiesToIgnore: TStringList;
procedure SetPropertiesToIgnore(const Value: TStrings);
function GetPropertiesToIgnore: TStrings;
protected
procedure SerializeSimpleType(const aTargetNode: IXMLNode; anObject: TObject; aPropertyInfo: PPropInfo);
procedure SerializeObject(const anObjectNode: IXMLNode; anObject: TObject; IsRoot: boolean = FALSE);
procedure DeserializeObject(const anObjectNode: IXMLNode; anObject: TObject; IsRoot: boolean = FALSE);
public
constructor Create(aOwner: TComponent); override;
destructor Destroy; override;
procedure Serialize(const aSource: TObject; const anObjectNode: IXMLNode; IsRoot: boolean = True); overload;
procedure Serialize(const aSource: TObject; aStream: TStream); overload;
procedure Deserialize(aStream: TStream; aDestination: TObject);
published
property StreamerOptions: TROXMLStreamerOptions read fXMLStreamerOptions write fXMLStreamerOptions;
property PropertiesToIgnore: TStrings read GetPropertiesToIgnore write SetPropertiesToIgnore;
end;
// Quick access functions
procedure SaveObjectToXMLNode(aSource: TObject; anObjectNode: IXMLNode; somePropertiesToIgnore: array of string; someStreamerOptions: TROXMLStreamerOptions = []; IsRoot: boolean = True);
procedure SaveObjectToStream(aSource: TObject; aStream: TStream; somePropertiesToIgnore: array of string; someStreamerOptions: TROXMLStreamerOptions = []);
procedure LoadObjectFromStream(aStream: TStream; aDestination: TObject; somePropertiesToIgnore: array of string);
procedure SaveObjectToFile(aSource: TObject; const aFileName: string; somePropertiesToIgnore: array of string; someStreamerOptions: TROXMLStreamerOptions = []);
procedure LoadObjectFromFile(const aFileName: string; aDestination: TObject; somePropertiesToIgnore: array of string);
// Dataset oriented functions
function DatasetToXML(
const aDataset : IDADataset;
const RootNodeName : string = '';
const RecordNodeName : string = 'Record';
IncludeSchema : boolean = TRUE) : IXMLDocument;
procedure XMLToCommandParams(const aCommand : IDASQLCommand; const XML : string);
function XMLToUpdateCommand(const aConnection : IDAConnection;
const aReferencedDataset : TDADataset;
const aTargetTableName : string;
const aXML : string) : IDASQLCommand;
implementation
uses SysUtils, uROTypes, Variants;
procedure SaveObjectToXMLNode(aSource: TObject; anObjectNode: IXMLNode; somePropertiesToIgnore: array of string; someStreamerOptions: TROXMLStreamerOptions = []; IsRoot: boolean = True);
var
i: integer;
begin
with TROXMLStreamer.Create(nil) do try
for i := 0 to High(somePropertiesToIgnore) do
PropertiesToIgnore.Add(somePropertiesToIgnore[i]);
StreamerOptions := someStreamerOptions;
Serialize(aSource, anObjectNode,IsRoot);
finally
Free;
end;
end;
procedure SaveObjectToStream(aSource: TObject; aStream: TStream; somePropertiesToIgnore: array of string; someStreamerOptions: TROXMLStreamerOptions = []);
var
i: integer;
begin
with TROXMLStreamer.Create(nil) do try
for i := 0 to High(somePropertiesToIgnore) do
PropertiesToIgnore.Add(somePropertiesToIgnore[i]);
StreamerOptions := someStreamerOptions;
Serialize(aSource, aStream);
finally
Free;
end;
end;
procedure LoadObjectFromStream(aStream: TStream; aDestination: TObject; somePropertiesToIgnore: array of string);
var
i: integer;
begin
with TROXMLStreamer.Create(nil) do try
for i := 0 to High(somePropertiesToIgnore) do
PropertiesToIgnore.Add(somePropertiesToIgnore[i]);
Deserialize(aStream, aDestination);
finally
Free;
end;
end;
procedure SaveObjectToFile(aSource: TObject; const aFileName: string; somePropertiesToIgnore: array of string; someStreamerOptions: TROXMLStreamerOptions = []);
var
fs: TFileStream;
begin
fs := TFileStream.Create(aFileName, fmCreate);
try
SaveObjectToStream(aSource, fs, somePropertiesToIgnore, someStreamerOptions);
finally
fs.Free;
end;
end;
procedure LoadObjectFromFile(const aFileName: string; aDestination: TObject; somePropertiesToIgnore: array of string);
var
fs: TFileStream;
begin
fs := TFileStream.Create(aFileName, fmOpenRead + fmShareDenyWrite);
try
LoadObjectFromStream(fs, aDestination, somePropertiesToIgnore);
finally
fs.Free;
end;
end;
{ TROXMLStreamer }
constructor TROXMLStreamer.Create(aOwner: TComponent);
begin
inherited;
fPropertiesToIgnore := TStringList.Create;
fPropertiesToIgnore.Sorted := TRUE;
fPropertiesToIgnore.Duplicates := dupError;
fXMLStreamerOptions := [soIncludeType];
end;
destructor TROXMLStreamer.Destroy;
begin
fPropertiesToIgnore.Free;
inherited;
end;
procedure TROXMLStreamer.Deserialize(aStream: TStream; aDestination: TObject);
var
xml: IXMLDocument;
begin
if (aStream = nil) then raise Exception.Create('Invalid stream');
xml := NewROXmlDocument;
xml.New('');
xml.LoadFromStream(aStream);
if (xml.DocumentNode.Value = ValueNIL) then
FreeAndNIL(aDestination)
else
DeserializeObject(xml.DocumentNode, aDestination, TRUE);
end;
procedure TROXMLStreamer.Serialize(const aSource: TObject; aStream: TStream);
var
xml: IXMLDocument;
begin
if (aStream = nil) then raise Exception.Create('Invalid stream');
xml := NewROXmlDocument;
try
if (aSource = nil) then begin
xml.New(ValueNIL);
Exit;
end
else
xml.New(aSource.ClassName);
// Serializes the object
SerializeObject(xml.DocumentNode, aSource, TRUE);
finally
xml.SaveToStream(aStream);
end;
end;
procedure TROXMLStreamer.SerializeSimpleType(const aTargetNode: IXMLNode; anObject: TObject; aPropertyInfo: PPropInfo);
var
int64val: int64;
intval: integer;
dblval: double;
strval: string;
{$IFNDEF DELPHI5}wstrval: widestring;
{$ENDIF}
lName: string;
begin
lName := {$IFDEF UNICODE}UTF8ToString{$ENDIF}(aPropertyInfo^.Name);
case aPropertyInfo^.PropType^.Kind of
{$IFDEF FPC}tkBool,{$ENDIF}
tkEnumeration: begin
//enuval := GetOrdProp(anObject, lName);
aTargetNode.Value := GetPropValue(anObject, lName, TRUE);
end;
tkInteger: begin
intval := GetOrdProp(anObject, lName);
aTargetNode.Value := intval;
end;
tkFloat: begin
dblval := GetFloatProp(anObject, lName);
aTargetNode.Value := dblval;
end;
tkLString,
{$IFDEF FPC}tkAString,{$ENDIF}
tkString: begin
strval := GetStrProp(anObject, lName);
aTargetNode.Value := strval;
end;
tkInt64: begin
int64val := GetInt64Prop(anObject, lName);
aTargetNode.Value := int64val;
end;
{$IFDEF UNICODE}tkUString,{$ENDIF}
tkWString: begin
{$IFDEF DELPHI5}
strval := GetStrProp(anObject, lName);
aTargetNode.Value := strval;
{$ELSE}
wstrval := GetWideStrProp(anObject, lName);
aTargetNode.Value := wstrval;
{$ENDIF}
end;
tkVariant, tkSet: begin
aTargetNode.Value := GetVariantProp(anObject, lName);
end;
else
raise Exception.Create(Format('Type not supported %s', [GetEnumName(TypeInfo(TTypeKind), Ord(aPropertyInfo^.PropType^.Kind))]))
end;
end;
procedure TROXMLStreamer.SerializeObject(const anObjectNode: IXMLNode; anObject: TObject; IsRoot: boolean = FALSE);
var
props: PPropList;
x, cnt, i: integer;
subnode, node: IXMLNode;
coll: TCollection;
// Temporary variables
objval: TObject;
pdata: PTypeData;
lname: string;
begin
if (anObject <> nil) and (anObject.ClassInfo <> nil) then begin
pdata := GetTypeData(anObject.ClassInfo);
if (pdata <> nil) then begin
cnt := pdata.PropCount;
if (cnt > 0) then begin
GetMem(props, cnt * SizeOf(PPropInfo));
try
cnt := GetPropList(PTypeInfo(anObject.ClassInfo), tkProperties, props);
for i := 0 to (cnt - 1) do begin
lName := {$IFDEF UNICODE}UTF8ToString{$ENDIF}(props^[i]^.Name);
if IsRoot and (fPropertiesToIgnore.IndexOf(lName) >= 0)
or not IsStoredProp(anObject, props^[i]) then Continue;
node := anObjectNode.Add(lName);
with props^[i]^ do
// Class types
if (PropType^.Kind = tkClass) then begin
node.AddAttribute(attr_Type, props^[i]^.PropType^.Name); // Always set fo object types
objval := GetObjectProp(anObject, lName);
node.AddAttribute(attr_IsObject, XMLBooleans[TRUE]);
if Assigned(objval) then begin
// TComponent
if (objval is TStrings) then begin
node.Value := TStrings(objval).Text;
end else
if (objval is TComponent) then begin
if (TComponent(objval).Owner = anObject) then
SerializeObject(node, objval) // Owned sub-component
else begin
// Reference to another component
node.AddAttribute(attr_ByRef, XMLBooleans[TRUE]);
node.AddAttribute(attr_RefName, TComponent(objval).Name);
if (TComponent(objval).Owner <> nil) then node.AddAttribute(attr_Owner, TComponent(objval).Owner.Name);
end;
end
// TCollection
else if (objval is TCollection) then begin
coll := TCollection(objval);
node.AddAttribute(attr_IsCollection, XMLBooleans[TRUE]);
node.AddAttribute(attr_Count, coll.Count);
node.AddAttribute(attr_ItemClass, coll.ItemClass.ClassName);
for x := 0 to (coll.Count - 1) do begin
subnode := node.Add(attr_Item + IntToStr(x));
SerializeObject(subnode, coll.Items[x]);
end;
end
// Standard persistent class
else
SerializeObject(node, objval);
end
// NIL object
else
node.AddAttribute(attr_IsNull, XMLBooleans[TRUE]);
end
// Simple types
else begin
if (soIncludeType in fXMLStreamerOptions) then node.AddAttribute(attr_Type, props^[i]^.PropType^.Name);
SerializeSimpleType(node, anObject, props^[i]);
end;
end;
finally
FreeMem(props, cnt * SizeOf(PPropInfo));
end;
end
(* // TROArray (they don't have properties so they will not fall in the previous block)
else if (anObject is TROArray) then begin
arr := TROArray(anObject);
anObjectNode.AddAttribute(attr_IsArray, XMLBooleans[TRUE]);
anObjectNode.AddAttribute(attr_Count, arr.Count);
if (arr.GetItemType<>NIL) then begin
anObjectNode.AddAttribute(attr_ItemClass, arr.GetItemType^.Name);
for x := 0 to (arr.Count-1) do begin
subnode := anObjectNode.Add(attr_Item+IntToStr(x));
SerializeObject(subnode, TObject(arr.GetItemRef(x)));
end;
end;
end*)
end;
end
else
anObjectNode.AddAttribute(attr_IsNull, XMLBooleans[TRUE]);
end;
procedure TROXMLStreamer.DeserializeObject(const anObjectNode: IXMLNode;
anObject: TObject; IsRoot: boolean = FALSE);
var
props: PPropList;
x, cnt, i: integer;
node: IXMLNode;
coll: TCollection;
// Temporary variables
objval: TObject;
pdata: PTypeData;
collitem: TCollectionItem;
strVal: string;
lName: string;
begin
if (anObjectNode.GetAttributeValue(attr_IsNull, XMLBooleans[FALSE]) = XMLBooleans[TRUE]) then begin
//anObject := NIL;
Exit;
end;
pdata := GetTypeData(anObject.ClassInfo);
if (pdata <> nil) then begin
cnt := pdata.PropCount;
if (cnt > 0) then begin
GetMem(props, cnt * SizeOf(PPropInfo));
try
cnt := GetPropList(PTypeInfo(anObject.ClassInfo), tkProperties, props);
for i := 0 to (cnt - 1) do begin
lname:= {$IFDEF UNICODE}UTF8ToString {$ENDIF}(props^[i]^.Name);
if IsRoot and (fPropertiesToIgnore.IndexOf(lName) >= 0) then Continue;
node := anObjectNode.GetNodeByName(lName);
if (node = nil) then Continue; // Property was not streamed
with props^[i]^ do
// Class types
if (PropType^.Kind = tkClass) then begin
objval := GetObjectProp(anObject, lName);
if (objval = nil) then
Continue
else begin
// TComponent
if (objval is TStrings) then begin
TStrings(objval).Text := node.Value;
end else
if (objval is TComponent) then begin
if (TComponent(objval).Owner = anObject) then
DeserializeObject(node, objval) // Owned sub-component
else begin
// Reference to another component
{node.AddAttribute(attr_ByRef, XMLBooleans[TRUE]);
node.AddAttribute(attr_RefName, TComponent(objval).Name);
if (TComponent(objval).Owner<>NIL)
then node.AddAttribute(attr_Owner, TComponent(objval).Owner.Name);}
end;
end
// TCollection
else if (objval is TCollection) then begin
coll := TCollection(objval);
coll.Clear;
for x := 0 to (node.ChildrenCount - 1) do try
if SameText(Copy(node.Children[x].Name, 1, 4), attr_Item) then begin
collitem := coll.Add;
DeserializeObject(node.Children[x], collitem);
end;
except
raise
end;
end
// Standard persistent class
else
DeserializeObject(node, objval);
end
end
// Simple types
else begin
case PropType^.Kind of
tkEnumeration: begin
// If enumerated values are empty, it should just skip and leave the defaults.
// Not doing so generates a range check error.
strVal := VarToStr(node.Value);
if (strVal<>'')
then SetPropValue(anObject, lName, GetEnumValue(props^[i]^.PropType{$IFNDEF FPC}^{$ENDIF}, strVal));
end;
else
SetPropValue(anObject, lName, node.Value);
end;
end;
end;
finally
FreeMem(props, cnt * SizeOf(PPropInfo));
end;
end
// TROArray (they don't have properties so they will not fall in the previous block)
(* else if (anObject is TROArray) then begin
arr := TROArray(anObject);
arr.Resize(anObjectNode.ChildrenCount-1);
if (arr.GetItemType<>NIL) then begin
arr.GetItemClass.Create
anObjectNode.AddAttribute(attr_ItemClass, arr.GetItemType^.Name);
for x := 0 to (arr.Count-1) do begin
subnode := anObjectNode.Add(attr_Item+IntToStr(x));
SerializeObject(subnode, TObject(arr.GetItemRef(x)));
end;
end;
end*)
end;
end;
procedure TROXMLStreamer.SetPropertiesToIgnore(const Value: TStrings);
begin
fPropertiesToIgnore.Assign(Value);
end;
function TROXMLStreamer.GetPropertiesToIgnore: TStrings;
begin
result := fPropertiesToIgnore;
end;
// Dataset oriented functions
function DatasetToXML(
const aDataset : IDADataset;
const RootNodeName : string = '';
const RecordNodeName : string = 'Record';
IncludeSchema : boolean = TRUE) : IXMLDocument;
var root, node : IXMLNode;
i : integer;
begin
result := NewROXmlDocument;
if (RootNodeName<>'') then result.New(RootNodeName)
else begin
if (Trim(aDataset.Name)<>'') then result.New(aDataset.Name)
else result.New('Dataset');
end;
if not aDataset.Active then Exit;
root := result.DocumentNode;
if IncludeSchema then begin
end;
while not aDataset.EOF do try
node := root.Add(RecordNodeName);
for i := 0 to aDataset.FieldCount-1 do begin
node.Add(aDataset.Fields[i].Name).Value := aDataset.Fields[i].AsString;
end;
finally
aDataset.Next;
end;
end;
procedure XMLToCommandParams(const aCommand : IDASQLCommand; const XML : string);
var xmldoc : IXMLDocument;
i : integer;
node : IXMLNode;
param : TDAParam;
begin
xmldoc := NewROXmlDocument;
xmldoc.New('CommandParams');
xmldoc.XML := XML;
for i := 0 to (aCommand.Params.Count-1) do
aCommand.Params[i].Value := Null;
for i := 0 to (xmldoc.DocumentNode.ChildrenCount-1) do begin
node := xmldoc.DocumentNode.Children[i];
param := aCommand.Params.FindParam(node.Name);
if (param=NIL) then Continue;
param.Value := node.Value;
end;
end;
function XMLToUpdateCommand(const aConnection : IDAConnection;
const aReferencedDataset : TDADataset;
const aTargetTableName : string;
const aXML : string) : IDASQLCommand;
const CRLF = #13#10;
var fld : TDAField;
i : integer;
xmldoc : IXMLDocument;
nme, where, sql : string;
begin
xmldoc := NewROXmlDocument;
xmldoc.New;
xmldoc.XML := aXML;
result := NIL;
if (xmldoc.DocumentNode.ChildrenCount=0)
then raise Exception.Create('Invalid XML document. Cannot generate an update statement');
sql := 'UPDATE '+aTargetTableName+' SET'+CRLF;
where := '';
with xmldoc do begin
for i := 0 to (DocumentNode.ChildrenCount-1) do begin
nme := DocumentNode.Children[i].Name;
fld := aReferencedDataset.Fields.FindField(nme);
if (fld=NIL) then begin
if (Pos(old_FieldPrefix, UpperCase(nme))=1) then begin
if (where<>'') then where := where+' AND ';
nme := Copy(nme, Length(old_FieldPrefix)+1, MaxInt);
fld := aReferencedDataset.Fields.FindField(nme);
if (fld=NIL) then Continue;
if (fld.DataType in [datWideString, datString, datDateTime])
then where := where+nme+'='+''''+VarToStr(DocumentNode.Children[i].Value)+''''
else where := where+nme+'='+VarToStr(DocumentNode.Children[i].Value);
end;
Continue;
end
else begin
sql := sql+fld.Name+'=';
if (fld.DataType in [datWideString, datString, datDateTime])
then sql := sql+''''+VarToStr(DocumentNode.Children[i].Value)+''''
else sql := sql+VarToStr(DocumentNode.Children[i].Value);
sql := sql+','+CRLF;
end;
end;
end;
sql := Copy(sql, 1, Length(sql)-3)+CRLF;
if (where<>'') then sql := sql+' WHERE '+where;
result := aConnection.NewCommand(sql, stSQL, 'UPDATE_'+aTargetTableName);
end;
procedure TROXMLStreamer.Serialize(const aSource: TObject; const anObjectNode: IXMLNode; IsRoot: boolean = True);
begin
if (anObjectNode = nil) then raise Exception.Create('Invalid XMLNode.');
if (aSource <> nil) then SerializeObject(anObjectNode, aSource, IsRoot);
end;
end.