226 lines
6.0 KiB
ObjectPascal
226 lines
6.0 KiB
ObjectPascal
unit XMLParse;
|
|
|
|
{ XML parser. Currently just calls MSXML 6.0 to do the real work. }
|
|
|
|
interface
|
|
|
|
uses
|
|
Windows, SysUtils, Variants;
|
|
|
|
type
|
|
IXMLNode = interface
|
|
function GetAttribute(const AName: String): String;
|
|
function GetOptionalAttribute(const AName: String): String;
|
|
function GetFirstChild: IXMLNode;
|
|
function GetNodeName: String;
|
|
function GetNextSibling: IXMLNode;
|
|
function GetNodeType: Integer;
|
|
function GetParentNode: IXMLNode;
|
|
function GetPreviousSibling: IXMLNode;
|
|
function GetRealMSXMLNode: OleVariant;
|
|
function GetText: String;
|
|
function HasAttribute(const AName: String): Boolean;
|
|
function TransformNode(const Stylesheet: IXMLNode): String;
|
|
property Attributes[const AName: String]: String read GetAttribute;
|
|
property OptionalAttributes[const AName: String]: String read GetOptionalAttribute;
|
|
property FirstChild: IXMLNode read GetFirstChild;
|
|
property NextSibling: IXMLNode read GetNextSibling;
|
|
property NodeName: String read GetNodeName;
|
|
property NodeType: Integer read GetNodeType;
|
|
property ParentNode: IXMLNode read GetParentNode;
|
|
property PreviousSibling: IXMLNode read GetPreviousSibling;
|
|
property Text: String read GetText;
|
|
end;
|
|
|
|
TXMLDocument = class
|
|
private
|
|
FDoc: OleVariant;
|
|
function GetRoot: IXMLNode;
|
|
public
|
|
constructor Create;
|
|
procedure LoadFromFile(const AFilename: String);
|
|
procedure StripComments;
|
|
property Root: IXMLNode read GetRoot;
|
|
end;
|
|
|
|
const
|
|
{ Values for the NodeType property }
|
|
NODE_INVALID = 0;
|
|
NODE_ELEMENT = 1;
|
|
NODE_ATTRIBUTE = 2;
|
|
NODE_TEXT = 3;
|
|
NODE_CDATA_SECTION = 4;
|
|
NODE_ENTITY_REFERENCE = 5;
|
|
NODE_ENTITY = 6;
|
|
NODE_PROCESSING_INSTRUCTION = 7;
|
|
NODE_COMMENT = 8;
|
|
NODE_DOCUMENT = 9;
|
|
NODE_DOCUMENT_TYPE = 10;
|
|
NODE_DOCUMENT_FRAGMENT = 11;
|
|
NODE_NOTATION = 12;
|
|
|
|
implementation
|
|
|
|
uses
|
|
ActiveX, ComObj;
|
|
|
|
type
|
|
TXMLNode = class(TInterfacedObject, IXMLNode)
|
|
private
|
|
FRealNode: OleVariant;
|
|
function GetFirstChild: IXMLNode;
|
|
function GetAttribute(const AName: String): String;
|
|
function GetOptionalAttribute(const AName: String): String;
|
|
function GetNextSibling: IXMLNode;
|
|
function GetNodeName: String;
|
|
function GetNodeType: Integer;
|
|
function GetParentNode: IXMLNode;
|
|
function GetPreviousSibling: IXMLNode;
|
|
function GetRealMSXMLNode: OleVariant;
|
|
function GetText: String;
|
|
function HasAttribute(const AName: String): Boolean;
|
|
function TransformNode(const Stylesheet: IXMLNode): String;
|
|
public
|
|
constructor Create(const ARealNode: OleVariant);
|
|
end;
|
|
|
|
function IsVarAssigned(const AVariant: OleVariant): Boolean;
|
|
begin
|
|
case VarType(AVariant) of
|
|
varEmpty: Result := False;
|
|
varDispatch: Result := Assigned(TVarData(AVariant).VDispatch);
|
|
else
|
|
raise Exception.Create('IsVarAssigned: Unexpected variant type');
|
|
end;
|
|
end;
|
|
|
|
function MakeNode(const ARealNode: OleVariant): IXMLNode;
|
|
begin
|
|
if IsVarAssigned(ARealNode) then
|
|
Result := TXMLNode.Create(ARealNode)
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function VariantToString(const V: OleVariant): String;
|
|
begin
|
|
if VarType(V) <> varOleStr then
|
|
raise Exception.Create('VariantToUTF8String: Expected varOleStr');
|
|
Result := TVarData(V).VOleStr;
|
|
end;
|
|
|
|
{ TXMLDocument }
|
|
|
|
constructor TXMLDocument.Create;
|
|
begin
|
|
inherited Create;
|
|
FDoc := CreateOleObject('MSXML2.DOMDocument.6.0');
|
|
FDoc.setProperty('ProhibitDTD', False);
|
|
FDoc.resolveExternals := True;
|
|
FDoc.async := False;
|
|
FDoc.preserveWhitespace := True;
|
|
end;
|
|
|
|
function TXMLDocument.GetRoot: IXMLNode;
|
|
begin
|
|
Result := MakeNode(FDoc.documentElement);
|
|
end;
|
|
|
|
procedure TXMLDocument.LoadFromFile(const AFilename: String);
|
|
begin
|
|
if not FDoc.load(AFilename) then begin
|
|
if Integer(FDoc.parseError.line) <> 0 then
|
|
raise Exception.CreateFmt('XML parse error (line %d, column %d): %s',
|
|
[Integer(FDoc.parseError.line), Integer(FDoc.parseError.linepos),
|
|
FDoc.parseError.reason])
|
|
else
|
|
raise Exception.CreateFmt('XML parse error: %s', [FDoc.parseError.reason]);
|
|
end;
|
|
end;
|
|
|
|
procedure TXMLDocument.StripComments;
|
|
begin
|
|
FDoc.selectNodes('//comment()').removeAll;
|
|
end;
|
|
|
|
{ TXMLNode }
|
|
|
|
constructor TXMLNode.Create(const ARealNode: OleVariant);
|
|
begin
|
|
inherited Create;
|
|
FRealNode := ARealNode;
|
|
end;
|
|
|
|
function TXMLNode.GetAttribute(const AName: String): String;
|
|
var
|
|
N: OleVariant;
|
|
begin
|
|
N := FRealNode.attributes.getNamedItem(AName);
|
|
if not IsVarAssigned(N) then
|
|
raise Exception.CreateFmt('Attribute "%s" does not exist', [AName]);
|
|
Result := VariantToString(N.value);
|
|
end;
|
|
|
|
function TXMLNode.GetOptionalAttribute(const AName: String): String;
|
|
var
|
|
N: OleVariant;
|
|
begin
|
|
N := FRealNode.attributes.getNamedItem(AName);
|
|
if not IsVarAssigned(N) then
|
|
Result := ''
|
|
else
|
|
Result := VariantToString(N.value);
|
|
end;
|
|
|
|
function TXMLNode.GetFirstChild: IXMLNode;
|
|
begin
|
|
Result := MakeNode(FRealNode.firstChild);
|
|
end;
|
|
|
|
function TXMLNode.GetNodeName: String;
|
|
begin
|
|
Result := VariantToString(FRealNode.nodeName);
|
|
end;
|
|
|
|
function TXMLNode.GetNextSibling: IXMLNode;
|
|
begin
|
|
Result := MakeNode(FRealNode.nextSibling);
|
|
end;
|
|
|
|
function TXMLNode.GetNodeType: Integer;
|
|
begin
|
|
Result := FRealNode.nodeType;
|
|
end;
|
|
|
|
function TXMLNode.GetParentNode: IXMLNode;
|
|
begin
|
|
Result := MakeNode(FRealNode.parentNode);
|
|
end;
|
|
|
|
function TXMLNode.GetPreviousSibling: IXMLNode;
|
|
begin
|
|
Result := MakeNode(FRealNode.previousSibling);
|
|
end;
|
|
|
|
function TXMLNode.GetRealMSXMLNode: OleVariant;
|
|
begin
|
|
Result := FRealNode;
|
|
end;
|
|
|
|
function TXMLNode.GetText: String;
|
|
begin
|
|
Result := VariantToString(FRealNode.text);
|
|
end;
|
|
|
|
function TXMLNode.HasAttribute(const AName: String): Boolean;
|
|
begin
|
|
Result := IsVarAssigned(FRealNode.attributes.getNamedItem(AName));
|
|
end;
|
|
|
|
function TXMLNode.TransformNode(const Stylesheet: IXMLNode): String;
|
|
begin
|
|
Result := VariantToString(FRealNode.transformNode(Stylesheet.GetRealMSXMLNode));
|
|
end;
|
|
|
|
end.
|