[Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Wrongly using TXMLDocument (D7)

Posted on 2006-05-24
27
Medium Priority
?
1,958 Views
Last Modified: 2007-11-27
I am working on an SVG charting unit and this is the beginning where I test and demo
all the routines to actually BUILD the SVG. I am not that familiar with TXMLDocument
as I have only worked with the MSXML DOM before and they are not anogolous.

Can someone help me figure out why this isn't working? oBase.nodevalue is always blank
and I get a "Only one top level element is allowed in an XML document." error
on the oDoc.appendChild(oBase); line.


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, xmldom, XMLIntf, msxmldom, XMLDoc, StdCtrls;
const
  FXMLNamespace  = 'http://www.w3.org/2000/svg';
  FDecimalFormat = '######0.0##';
  CHART_SCRIPT_BLOCK = 'chartspecificscript';
  CONST_PIE_ELEMENT = 'PieElement';

type
  ESVGClassesError = class(Exception);

  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    FXMLDoc: TXMLDocument;
    procedure GetBaseSVG;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure InsertAttribute(var iNode: IDOMNode; AAttributeName, AAttributeValue: String);
var
  iAttr : IDOMNode;
begin
  iAttr := iNode.ownerDocument.createAttribute(AAttributeName);
  iAttr.nodeValue := AAttributeValue;
  iNode.attributes.setNamedItem(iAttr);
end;

procedure InsertGroup(var iNode: IDOMNode; AID: String);
var
  oNode : IDOMNode;
begin
  oNode := iNode.ownerDocument.createElement('g');
  if Length(AID) > 0 then
  begin
    InsertAttribute(oNode, 'id', AID);
  end;
  iNode.appendChild(oNode);
end;

procedure InsertRect(var iNode: IDOMNode; X, Y, AWidth, AHeight: Double; AStroke: String; AStrokeWidth: Double; AFill: String);
var
  oNode: IDOMNode;
begin
  oNode := iNode.ownerDocument.createElement('rect');

  InsertAttribute(oNode, 'x', FormatFloat(FDecimalFormat, X));
  InsertAttribute(oNode, 'y', FormatFloat(FDecimalFormat, Y));
  InsertAttribute(oNode, 'width', FormatFloat(FDecimalFormat, AWidth));
  InsertAttribute(oNode, 'height', FormatFloat(FDecimalFormat, AHeight));

  if Length(AStroke) > 0 then
  begin
    InsertAttribute(oNode, 'stroke', AStroke);
    InsertAttribute(oNode, 'stroke-width', FormatFloat(FDecimalFormat, AStrokeWidth));
  end;

  if Length(AFill) > 0 then
  begin
    InsertAttribute(oNode, 'fill', AFill);
  end;
  iNode.appendChild(oNode);
end;


procedure TForm1.GetBaseSVG;
var
  sBase: String;
begin
  sBase := '<svg xmlns="' + FXMLNamespace + '" width="' +
           FormatFloat('000.0', 300) + '" height="' +
           FormatFloat('000.0', 300) + '" viewBox="0 0 ' +
           FormatFloat('#####', 300) + ' ' +
           FormatFloat('#####', 300) + '" xml:space="preserve"></svg>';
  try
    FXMLDoc.XML.Text := sBase;
  except
    raise ESVGClassesError.Create('Cannot load Base XML Document.');
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  oDoc: IDOMDocument;
  oBase: IDOMNode;
begin
  GetBaseSVG;
  FXMLDoc.Active := True;
  oDoc := FXMLDoc.DOMDocument;
  oBase := oDoc.getElementsByTagName('svg').item[0];
  InsertGroup(oBase, 'ChartBox');
  InsertRect(oBase, 100, 100, 200, 200, 'black', 1, '#EEEEDD');
  oDoc.appendChild(oBase);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FXMLDoc          := TXMLDocument.Create(nil);
  FXMLDoc.Active   := True;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FXMLDoc.Free;
end;

end.
0
Comment
Question by:EddieShipman
  • 14
  • 11
  • 2
27 Comments
 
LVL 28

Expert Comment

by:2266180
ID: 16752296
you code doesn't run out of the box.
  oBase := oDoc.getElementsByTagName('svg')
from procedure TForm1.Button1Click(Sender: TObject);
returns 0 elements.
I'm investigating, but I'm teling you in case you missed something
0
 
LVL 26

Author Comment

by:EddieShipman
ID: 16752324
It is empty, I am ADDING elements to it in InsertGroup/InsertRect.
oBase is not nil, check the nodevalue.
0
 
LVL 28

Expert Comment

by:2266180
ID: 16752378
well .. I got an access violation and checked, so it's nill on my side. do I have to do anything else except start the app and click the button?
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 28

Accepted Solution

by:
2266180 earned 1800 total points
ID: 16752579
I modified a little bit the source and added some comments: hope that clarifies it :)

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, xmldom, XMLIntf, msxmldom, XMLDoc, StdCtrls;
const
  FXMLNamespace  = 'http://www.w3.org/2000/svg';
  FDecimalFormat = '######0.0##';
  CHART_SCRIPT_BLOCK = 'chartspecificscript';
  CONST_PIE_ELEMENT = 'PieElement';

type
  ESVGClassesError = class(Exception);

  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    FXMLDoc: TXMLDocument;
    procedure GetBaseSVG;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure InsertAttribute(var iNode: IDOMNode; AAttributeName, AAttributeValue: String);
var
  iAttr : IDOMNode;
begin
  iAttr := iNode.ownerDocument.createAttribute(AAttributeName);
  iAttr.nodeValue := AAttributeValue;
  iNode.attributes.setNamedItem(iAttr);
end;

function InsertGroup(var iNode: IDOMNode; AID: String):IDOMNode;
var
  oNode : IDOMNode;
begin
  oNode := iNode.ownerDocument.createElement('g');
  if Length(AID) > 0 then
  begin
    InsertAttribute(oNode, 'id', AID);
  end;
  result:=iNode.appendChild(oNode);
end;

function InsertRect(var iNode: IDOMNode; X, Y, AWidth, AHeight: Double; AStroke: String; AStrokeWidth: Double; AFill: String):IDOMNODE;
var
  oNode: IDOMNode;
begin
  oNode := iNode.ownerDocument.createElement('rect');

  InsertAttribute(oNode, 'x', FormatFloat(FDecimalFormat, X));
  InsertAttribute(oNode, 'y', FormatFloat(FDecimalFormat, Y));
  InsertAttribute(oNode, 'width', FormatFloat(FDecimalFormat, AWidth));
  InsertAttribute(oNode, 'height', FormatFloat(FDecimalFormat, AHeight));

  if Length(AStroke) > 0 then
  begin
    InsertAttribute(oNode, 'stroke', AStroke);
    InsertAttribute(oNode, 'stroke-width', FormatFloat(FDecimalFormat, AStrokeWidth));
  end;

  if Length(AFill) > 0 then
  begin
    InsertAttribute(oNode, 'fill', AFill);
  end;
  result:=iNode.appendChild(oNode);
end;


procedure TForm1.GetBaseSVG;
var
  sBase: String;
begin
  sBase := '<svg xmlns="' + FXMLNamespace + '" width="' +
           FormatFloat('000.0', 300) + '" height="' +
           FormatFloat('000.0', 300) + '" viewBox="0 0 ' +
           FormatFloat('#####', 300) + ' ' +
           FormatFloat('#####', 300) + '" xml:space="preserve"></svg>';
  try
    FXMLDoc.XML.Text := sBase;
  except
    raise ESVGClassesError.Create('Cannot load Base XML Document.');
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  oDoc: IDOMDocument;
  oBase: IDOMNode;
begin
  GetBaseSVG;
  FXMLDoc.Active := True;
  oDoc := FXMLDoc.DOMDocument;

  oBase := oDoc.childNodes.item[0];
  oBase:=InsertGroup(oBase, 'ChartBox');
  // the insert rect must be done in the newly added chartbox. so we need that node.
  oBase:=InsertRect(oBase, 100, 100, 200, 200, 'black', 1, '#EEEEDD');
  showmessage(oDoc.childNodes.item[0].childNodes.item[0].childNodes.item[0].localname);
//  oDoc.appendChild(oBase);// no need for this. the elements are already added
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FXMLDoc          := TXMLDocument.Create(nil);
  FXMLDoc.Active   := True;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FXMLDoc.Free;
end;

end.


I hope this is what you wanted :) if the levels are not the correct ones, you can adjust by making sure that you "isert" in the correct node ;)
0
 
LVL 26

Author Comment

by:EddieShipman
ID: 16752672
no. IT worked fine for me.
0
 
LVL 26

Author Comment

by:EddieShipman
ID: 16752691
I will try shortly.
0
 
LVL 28

Expert Comment

by:2266180
ID: 16752804
maybe we have different msxml parsers? afterall, txmldocument is just a wrapper on that. but I'm teling you, winxp2 sp2, d7 everythiing on default, and that getElementsByTagName returned 0 elements (however the element was there)
0
 
LVL 26

Author Comment

by:EddieShipman
ID: 16753264
But after modifying oBase, FXMLDoc.XML.Text still doesn't have the added nodes, only the BaseSVG.
0
 
LVL 26

Author Comment

by:EddieShipman
ID: 16753273
I'm trying to show the resultant XML text in the memo:

Memo1.Lines.text :=  FXMLDoc.XML.Text; at the end of Button1Click.
0
 
LVL 28

Expert Comment

by:2266180
ID: 16753354
hm... I'll look into it after I get home: about 40-60 min
0
 
LVL 26

Author Comment

by:EddieShipman
ID: 16753418
ok, I'll be going to lunch soon.
0
 
LVL 28

Expert Comment

by:2266180
ID: 16754116
ok. well .. it seems that for some reason the XML proeprty is not updated do the following:

procedure TForm1.Button1Click(Sender: TObject);
var
  oDoc: IDOMDocument;
  oBase: IDOMNode;
begin
  GetBaseSVG;
  FXMLDoc.Active := True;
  oDoc := FXMLDoc.DOMDocument;
  oBase := oDoc.childNodes.item[0];
  oBase:=InsertGroup(oBase, 'ChartBox');
  // the insert rect must be done in the newly added chartbox. so we need that node.
  oBase:=InsertRect(oBase, 100, 100, 200, 200, 'black', 1, '#EEEEDD');
  showmessage(oDoc.childNodes.item[0].childNodes.item[0].childNodes.item[0].localname);
  FXMLDoc.SaveToFile('c:\test.xml');
//  oDoc.appendChild(oBase);// no need for this. the elements are already added
end;

and check the file: it will have the nodes :)

I'll try and see why the xml property isn't updated. don't have a clue right now
0
 
LVL 26

Author Comment

by:EddieShipman
ID: 16754982
I do not want to save the file.
0
 
LVL 28

Expert Comment

by:2266180
ID: 16755029
:)) you don't have to. that was just an example that could be "checked".
save it to a memorystream and then read the string from there. it's an ugly workaround, but it is a workaround.
I'll update you as soon as I figure out the issue with that property.
0
 
LVL 26

Author Comment

by:EddieShipman
ID: 16755138
If you know how to do this with the std msxml dom, that would probably be easier
than trying to debug Borland's poor excuse for a wrapper.
0
 
LVL 28

Expert Comment

by:2266180
ID: 16755223
I agree with you. but I am not exactly sure that I understood what you want to do :)
could you explain a little more?
0
 
LVL 26

Author Comment

by:EddieShipman
ID: 16755639
use IXMLDocument2 .vs TXMLDocument
0
 
LVL 28

Expert Comment

by:2266180
ID: 16756338
so I am understanding that you want to use IXMLDocument2 instead of TXMLDocument. would
uses msxml;
help then?
or am I still on the wrong track? :)
0
 
LVL 26

Author Comment

by:EddieShipman
ID: 16760826
well, in order to use IXMLDocument2 instead of TXMLDocument, then, yes, you would
put MSXML or MSXML2_TLB in the uses.

However, the variables would have different types and the function calls would return different
object types.
0
 
LVL 28

Expert Comment

by:2266180
ID: 16761353
true, but since you haven't started much, it shouldn't be much of a problem, no?
0
 
LVL 26

Author Comment

by:EddieShipman
ID: 16763165
OK, I've got it working now. Only problem is, I can't show it in the SVG ActiveX. Any help with that?
I imported the SVG ActiveX and dropped a SVGCtl control on the form and tried to set the SRC property
to the file. Personally, I don't want to have to save to a file but I don't know if the ActiveX will allow
you to load the SVGSource directly, I will check the Adobe forums.


Here's the source:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, MSXML2_TLB, COMObj, SHDocVw, OleCtrls;

const
  FXMLNamespace  = 'http://www.w3.org/2000/svg';
  FDecimalFormat = '######0.0##';
  CHART_SCRIPT_BLOCK = 'chartspecificscript';
  CONST_PIE_ELEMENT = 'PieElement';

type
  ESVGClassesError = class(Exception);

  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    WebBrowser1: TWebBrowser;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    FXMLDoc: IXMLDOMDocument2;
    procedure GetBaseSVG;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure InsertAttribute(var iNode: IXMLDOMNode; AAttributeName, AAttributeValue: String);
var
  iAttr : IXMLDOMAttribute;
begin
  iAttr := iNode.ownerDocument.createAttribute(AAttributeName);
  iAttr.nodeValue := AAttributeValue;
  iNode.attributes.setNamedItem(iAttr);
end;

function InsertGroup(var iNode: IXMLDOMNode; AID: String):IXMLDOMNode;
var
  oNode : IXMLDOMNode;
begin
  oNode := iNode.ownerDocument.createNode(NODE_ELEMENT, 'g', FXMLNamespace);
  if Length(AID) > 0 then
  begin
    InsertAttribute(oNode, 'id', AID);
  end;
  Result := iNode.appendChild(oNode);
end;


function InsertRect(var iNode: IXMLDOMNode; X, Y, AWidth, AHeight: Double; AStroke: String; AStrokeWidth: Double; AFill: String): IXMLDOMNode;
var
  oNode: IXMLDOMNode;
begin
  oNode := iNode.ownerDocument.createNode(NODE_ELEMENT, 'rect', FXMLNamespace);
  InsertAttribute(oNode, 'x', FormatFloat(FDecimalFormat, X));
  InsertAttribute(oNode, 'y', FormatFloat(FDecimalFormat, Y));
  InsertAttribute(oNode, 'width', FormatFloat(FDecimalFormat, AWidth));
  InsertAttribute(oNode, 'height', FormatFloat(FDecimalFormat, AHeight));

  if Length(AStroke) > 0 then
  begin
    InsertAttribute(oNode, 'stroke', AStroke);
    InsertAttribute(oNode, 'stroke-width', FormatFloat(FDecimalFormat, AStrokeWidth));
  end;

  if Length(AFill) > 0 then
  begin
    InsertAttribute(oNode, 'fill', AFill);
  end;
  Result := iNode.appendChild(oNode);
end;


procedure TForm1.GetBaseSVG;
var
  sBase: String;
begin
  sBase :=  '<?xml version="1.0" encoding="iso-8859-1"?>' + #13 +
            '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20001102//EN" "http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd">' + #13 +
            '  <svg xmlns="' + FXMLNamespace + '" width="' +
             FormatFloat('000.0', 300) + '" height="' +
             FormatFloat('000.0', 300) + '" viewBox="0 0 ' +
             FormatFloat('#####', 300) + ' ' +
             FormatFloat('#####', 300) + '" xml:space="preserve">'+ #13 +
             '  </svg>';
  try
    FXMLDoc.LoadXML(sBase);
  except
    raise ESVGClassesError.Create('Cannot load Base XML Document.');
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  oDoc      : IXMLDOMElement;
  oBase     : IXMLDOMNode;
  oGroup    : IXMLDOMNode;
  oRect     : IXMLDOMNode;
  oNodeList : IXMLDOMNodeList;
begin
  GetBaseSVG;
  oDoc      := FXMLDoc.documentElement;
  oNodeList := oDoc.selectNodes('//svg');
  oBase     := oNodeList.item[0];
  oGroup    := InsertGroup(oBase, 'ChartBox');
  oRect     :=InsertRect(oBase, 100, 100, 200, 200, 'black', 1, '#EEEEDD');
  oBase.appendChild(oGroup);
  oBase.appendChild(oRect);
  Memo1.Lines.Text := FXMLDoc.xml;
  Memo1.Lines.SaveToFile('c:\temp\test.svg');
  WebBrowser1.Navigate('C:\Temp\test.svg');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FXMLDoc := CreateOleObject('MSXML2.DOMDocument.3.0') as IXMLDOMDocument2;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FXMLDoc := nil;
end;

end.

0
 
LVL 28

Expert Comment

by:2266180
ID: 16764761
one question. I tried to import msxml2 but didn't find it in the list. what do I need to have installed for that?
0
 
LVL 27

Assisted Solution

by:BigRat
BigRat earned 200 total points
ID: 16783397
>>I tried to import msxml2 but didn't find it in the list. what do I need to have installed for that?

Import TYPE LIBRARY and give the file name as msxml2.dll under system32.

>>Only problem is, I can't show it in the SVG ActiveX. Any help with that?

   oNodeList := oDoc.selectNodes('//svg');

selects nodes whose namespace is empty. Yet the nodes were created with "createNode(NODE_ELEMENT, 'g', FXMLNamespace)" and "<svg xmlns="' + FXMLNamespace + '" width="', ie: a namespace.

So before selecting one must  specify the name spaces :-

   xsdDOM.setProperty('SelectionNamespaces','xmlns:xsd="http://www.w3.org/1999/XMLSchema"');
   Target:=xsdDOM.selectSingleNode('/xsd:schema/@targetNamespace');

is an example of me handling validation by schema.

HTH
0
 
LVL 26

Author Comment

by:EddieShipman
ID: 16790163
BigRat, what does selcting the nodes with no namespace have to do with showing the SVG in the ActiveX? BTW, It DOES return a valid node anyway.
0
 
LVL 27

Expert Comment

by:BigRat
ID: 16790958
I just saw the selectNodes bit and noticed that they had a namespace so I posted that comment.

I don't see any activeX object in your last post(?)
0
 
LVL 26

Author Comment

by:EddieShipman
ID: 16791211
The selectnodes call works just fine as it is.

[quote]I imported the SVG ActiveX and dropped a SVGCtl control on the form and tried to set the SRC property
to the file. Personally, I don't want to have to save to a file but I don't know if the ActiveX will allow
you to load the SVGSource directly, [/quote]

I did not have it in my posted source because it did not work.
0
 
LVL 26

Author Comment

by:EddieShipman
ID: 16847060
I took another approach but your post led me to some corret code.
L8r...

0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
This Micro Tutorial will teach you how to add a cinematic look to any film or video out there. There are very few simple steps that you will follow to do so. This will be demonstrated using Adobe Premiere Pro CS6.
With just a little bit of  SQL and VBA, many doors open to cool things like synchronize a list box to display data relevant to other information on a form.  If you have never written code or looked at an SQL statement before, no problem! ...  give i…
Suggested Courses
Course of the Month20 days, 10 hours left to enroll

868 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question