VerySimpleXML – a lightweight Delphi XML reader and writer

Thursday, November 10th, 2011 | Delphi 2009, Delphi 2010, Delphi Programming, Delphi XE, Delphi XE2

There are lot of possibilities if you’re in need to parse or write XML files:

Now here comes another one: VerySimpleXML – a lightweight, one-unit XML reader/writer in under 500 600 lines of code. Use it for small well-formed XML files (like configuration files, etc.).

Download verysimplexml-v1.1.zip (7kb)
Download verysimplexml-v1.0.zip (7kb)

What’s new:
v1.1 – 2012/05/01
changed class name to reflect unit naming (class is now called
TXmlVerySimple instead TVerySimpleXml)
- it uses TStreamReader to read line by line instead of reading the
whole file at once (this allows parsing of big xml files with little
additional memory)
- automatically escapes/unescapes not allowed chars (>, <, ", &), thus
removes the most-hated "- escaping introduced in v1.0

Some usage examples:

uses Xml.VerySimple;
 
var
  Xml: TXmlVerySimple;
  Node, Child: TXmlNode;
begin
  Xml := TXmlVerySimple.Create;
  Xml.LoadFromFile('example.xml');
  Xml.Root.Find('book', 'id', 'bk102').Find('author').Text := 'Dein, Carol';
  Node := Xml.Root.AddChild('book');
  Child := Node.AddChild('author');
  Child.Text := 'Barger, Al';
  Child.Attribute['type'] := 'topseller';
  Node.AddChild('title').SetAttribute('lang', 'en').SetText('A big View');
  Xml.SaveToFile('output.xml');
  Xml.Free;
end;

VerySimpleXML supports just a subset of the XML specification

  • load and save from stream or file
  • nodes, childs and attributes
  • UTF-8 and ANSI encoding
  • compact output by setting Xml.Ident := ”;
  • method chaining
  • “>” and “>” inside text and attribute values when wrapped in quotation marks (XML-spec requires you to transform them into &lt; etc.)now automatically escaped with v1.1

It does NOT support:

  • CDATA, comments, etc…

Example XML-file:

<?xml version="1.0" encoding="utf-8" ?>
<catalog>
  <book id="bk101">
    <author>Gambardella, Matthew</author>
    <title/>
    <keywords />
  </book>
  <book id=bk102 lang="en">
    <author>Ralls, Kim</author>
    <title>Midnight Rain</title>
    <description>A former architect battles corporate zombies</description>
    <keywords>
      <keyword lang="es">no-muerto</keyword>
      <keyword lang=en>zombies</keyword>
    </keywords>
  </book>
  <book id="bk103">
    <author rate="&gt;5">Corets, Eva</author>
    <title>Maeve Ascendant</title>
    <description>The collapse of a <nanotechnology> society</description>
  </book>
</catalog>

Warning! This is not a standard well-formed XML file (not all attributes are wrapped with quotation marks, < and > are used within attributes and text), it is an example of the (very simple) fault tolerance of VerySimpleXML. Version 1.1 automatically escapes all non allowed chars.

Tags: ,

45 Comments to VerySimpleXML – a lightweight Delphi XML reader and writer

Jameel Halabi
November 10, 2011

Nice little unit!

I made an adaptation to let it work in FreePascal :)

It’s working right now with FreePascal 2.6.0rc1 which introduced generics syntax compatibility with Delphi, still I need to adapt unicode strings to work with FreePascal style unicode strings, but for now it’s working nicely :)

http://www.mediafire.com/?7erb3crlbk2n5op

Carlos Tré
November 10, 2011

Very nice indeed, thank you very much! :-)

Broto Suseno
November 11, 2011

Is the example a valid XML? IE won’t display, FF shows many errors. After fixing question mark in xml tag and some quotes attributes errors, the feature of storing (node) text wrapped in quotation marks is still complained by FF. Am I missing something? Thanks

Dennis
November 11, 2011

@Broto:
the question mark in the xml tag was just a typo in the example (fixed).

XML-Spec for “well-formed” files requires the attributes wrapped in quotation marks.
VerySimpleXML parses well even without them (if they consist of a single word).

The other non-XML-standard behavior of VerySimpleXML is the usage of the < and > tags inside an attribute or text:
XML spec requires you to write &lt; and &gt; but VerySimpleXML parses well if wrapped in quotation marks.

The XML example just shows these additional VerySimpleXML features. If you feed it with standard well-formed XML files, they will be parsed as well, of course ;-)

danial
November 11, 2011

I has the same issues but was able to fix via validation, i have started using a new xml editor I found recently, http://www.liquid-technologies.com/xml-editor.aspx, anyone know where I can get this for free?

Pol
November 14, 2011

Nice API. Not sure if supporting invalid XML is good idea but at some point it may be useful so thanks for this *subset*-XML and *very subset*-SGML parser ;)

Menjanahary R. R.
November 18, 2011

So nice ! I’ll give it a try. I’ve used Delphi native XML, OmniXML, NativeXML to handle gpx file. Lightweight is appealing.

William Meyer
January 20, 2012

I wonder whether your code can provide me a means of achieving XML of the form:

I have battled with TXMLDocument, with no success. All the examples I have seen will get me something of this form:

But that is unacceptable.

Thanks for your assistance!

William Meyer
January 20, 2012

I wonder whether your code can provide me a means of achieving XML of the form:

I have battled with TXMLDocument, with no success. All the examples I have seen will get me something of this form:

…”

But that is unacceptable.

Thanks for your assistance!

William Meyer
January 20, 2012

My apologies. Apparently I cannot insert XML text in the comment.

Dennis
January 21, 2012

try using [] instead of &glt;

Philip Hutchionson
January 24, 2012

Hi,
I cannot get this code to compile into delphi 7 – do you have a version that will work in 7?

Dennis
January 27, 2012

@Philip:
It uses generics, but it should not be that difficult to rewrite those parts without generics (you need to manually free the list objects).

[...] VerySimpleXML - очень и очень простой парсер. С ним, собственно, я и начал работать сначала из-за его простоты. [...]

Eric
March 23, 2012

Hi, Very nice and elegant code.

I wonder if you can add an alternative to FindNodes that takes an Anonymous method as parameter, so that you can loop the nodes without having to create and free a temporary TXmlNodeList.

Something like:
TXmlNodeCallBack = reference to procedure(node : TXmlNode);

procedure TXmlNode.ScanNodes(Name: String; CallBack : TXmlNodeCallBack);
var
Node: TXmlNode;
begin
Name := lowercase(Name);
for Node in ChildNodes do
if (lowercase(Node.NodeName) = Name) then
CallBack(Node);
end;

Philippe Wate
April 3, 2012

Thanks for this great code.
It seems you have a small bug when the file has

and you want to write it back
check it out – I can submit you with example if need be
regards
PW

Philippe Watel
April 19, 2012

Given that it is private
FAttributes: TXmlAttributeList;

I would be nice to have methods to able to enumerate attributes captions and values for a node
You can set and get only as long as you know their names
which you might not know ……
Thank you
best

Dennis
April 20, 2012

You should know all possible attributes of a node – I can’t imagine an example where you’d need a “dynamic unknown” attribute collection?

Philippe Watel
April 26, 2012

Something like that would be acceptable??

property AttibuteCount:integer read GetAttibuteCount;
function ListAttributeNames(Items: Tstrings): Integer;
function ListAttributeValues(Items: Tstrings): Integer;

function TXmlNode.GetAttibuteCount: integer;
begin
if not assigned(FAttributes) then
Exit(0);
Result := FAttributes.Count;
end;

function TXmlNode.ListAttributeNames(Items: Tstrings): Integer;
var
Attribute: TXmlAttribute;
begin
Items.Clear;
if not assigned(FAttributes) then
Exit(0);
for Attribute in Self.FAttributes do
Items.Add(Attribute.Name);
Result := FAttributes.Count;
end;

function TXmlNode.ListAttributeValues(Items: Tstrings): Integer;
var
Attribute: TXmlAttribute;
begin
Items.Clear;
if not assigned(FAttributes) then
Exit(0);
for Attribute in Self.FAttributes do
Items.Add(Attribute.Value);
Result := FAttributes.Count;
end;

Philippe Watel
April 26, 2012

Sorry it should on on a nodelist not a node

Philippe Watel
May 9, 2012

How do you please delete a node

I find it and then I free it but then it cannot save to file
aNode := x.NodeExist(‘flavors’);
if (aNode nil) then
aNode := aNode.Find(‘ROW’, ‘caption’, ‘World’);
if (aNode nil) then
aNode.Free;

then xml.savetofile crashes
any ideas thanks

Dennis
May 10, 2012


...
if (aNode <> nil) then
begin
// remove node from parent childnodes
if (assigned(aNode.Parent)) then
aNode.Parent.Childnodes.Remove(aNode)
end;

If you remove a node, all you have to do is to remove it from the parent childnodes and the node will be freed by the removal procedure.
As only the root node has no parent, you may even omit the “assigned check”, as a “find” call won’t return the root node itself:


...
if (aNode <> nil) then
// remove node from parent childnodes, the node itself will be freed then
aNode.Parent.Childnodes.Remove(aNode);

x2nie
June 6, 2012

Hi Dennis, how to use / test this unit within Delphi 7?

Thanks!

Dennis
June 6, 2012

@x2nie:
As it uses generics, you have to rewrite those generic calls:

e.g.:
replace
TObjectList(TXmlNode) with TList and remove the nodes in the TXmlNode.destroy method “by hand”.

JonR
June 17, 2012

@Dennis:
Delphi 7 has a TObjectList in the Contnrs unit, which has an OwnsObjects property and will free objects when they are removed from the list. You just can’t have a Generics version, so items have to be cast back to TXmlNode.

Pierre
June 18, 2012

Hello Dennis,

I just tried your unit today. I am very new to xml. I don’t know if I messed something up while adapting it to delphi XE, but if I change the value in the BtnGetClick procedure, I get an AV.

for instance :

ShowMessage(Xml.Root.Find(‘book’, ‘id’, ‘bkYYY’).Find(‘description’).Text);

Shouldn’t it not return a value at all or a nil, instead of an AV?
Regards

Dennis
June 18, 2012

@Pierre:
If you’re using fluent interface/method chaining you should be aware of NIL return types (resulting in exceptions), e.g.

ShowMessage(Xml.Root.Find(‘book’, ‘id’, ‘bkYYY’).Find(‘description’).Text);

is the short form of


Node := Xml.Root.Find('book','id','bkYYY');
Child := Node.Find('description');
showmessage(Child.Text);

thus, if Node is NIL (because there is no book with id=bkYYY) then you’ll get an AV right after the Node.Find method call.
Use method chaining with caution and only if you know how your data is structured.
To be sure you should use it like this:


Node := Xml.Root.Find('book','id','bkYYY');
if assigned(Node) then
begin
Child := Node.Find('description');
if assigned(Child) then
showmessage(Child.Text);
end;

Pierre
June 18, 2012

@Dennis:
Ok, I get it. You have done a very good job making verysimplexml. Very easy to use, once you get your head around xml.

Thanks a lot.

[...] XML fetching using XML.VerySimple library [...]

[...] saya menemukan sebuah komponen XML parser/XML konstruktor yang cukup stabil untuk dipakai: XML.VerySimple. Komponen ini awalnya dinamakan VerySimpleXML, tapi entah kenapa pembuatnya menamai dengan nama baru [...]

ta
September 18, 2012

[Help required]I want to add new “childnode” in the XML file if this childnode is not exist in the file. Is there any function to search the “value” of a childnode in this VerySimpleXML library?

Plz. help me. Thanks

johndoo
September 26, 2012

Hello,

I’ve tried to compile the unit under last version of Lazarus but there are many errors (each time I find a solution with one error, another fires …).

Anyone successfully modified this unit in order to use it with lazarus ?

Thanks

Thomas B.
November 3, 2012

Hello!

I have a stupid question: how can I walk from one node to the next until the end of file is reached?

Thanks

Dennis
November 5, 2012

take a look at the included example:

// Add new keyword attribute (lang=en) to every book
AllNodes := Xml.Root.FindNodes('book');
for BookNode in AllNodes do
begin
Nodes := BookNode.Find('keywords').FindNodes('keyword');
for Node in Nodes do
if not Node.HasAttribute('lang') then
Node.Attribute['lang'] := 'en';
Nodes.Free;
end;
AllNodes.Free;

or you'd just use the Node.ChildNodes list:


MyNode := Xml.Root.FindNode('parentnode');
for NextNode in MyNode.ChildNodes do
begin end;

if you don't know the parentnode, then fetch it by
calling MyNode.Parent

(see included example)

Alain M.
December 10, 2012

Hello,
When i use Node.Parent.ChildNodes.Remove(Node);
in fact remove an object from Tlist
I’ve always an access violation.
I think something wrong in list but where ?
is it ok for you?
An idea ? thanks

Janvh
January 17, 2013

Hi Dennis,
Thanks for the great work. Lightweight and very flexible. It suits my needs just fine.

Two question however:
1. do you plan to add support for comments?
2. I added a property Attributes: TXmlAttributeList read FAttributes;
to get the attribute count. Do you see any problems in that?

Thanks again!

Dennis
January 21, 2013

@Janvh:
1. I don’t think so. Including comments would require a lot of more code (you could place a comment EVERYWHERE in the document).
2. Sure, that’s quite ok. I’ve added it already in the next code release cycle

Erik
February 3, 2013

It would be cool if comments in XML files were ignored. I have an XML file with a comment line before the RootNode. This comment line becomes the RootNode as the line is treated like a Tag.

Peter
February 23, 2013

Hello all!

There is a small Delphi-TXMLDocument incompatibility in v1.1:

Attribute must read Attributes.

Peter
February 23, 2013

Hi!
I have added some routines to make VerySimpleXML interface more compatible to TXMLDocument.

type
TXmlNodeList = class(TObjectList)
function Get(i: Integer): TXmlNode;
function FindNode(const ANodeName: String): TXMLNode;
end;

function TXmlNodeList.Get(i: Integer): TXmlNode;
begin
Result := Items[i];
end;

function TXmlNodeList.FindNode(const ANodeName: String): TXMLNode;
var
i: Integer;
begin
Result := nil;
for i := 0 to Count-1 do
begin
if (Items[i].NodeName = ANodeName) then
begin
Result := Items[i];
Break;
end;
end;
end;

function TXmlVerySimple.AddChild(const Name: String): TXmlNode;
begin
Result := Root.AddChild(Name);
end;

_Robert
March 27, 2013

Too bad this doesn’t compile in Delphi 7. oh well, DIXml to the rescue.

Dennis
March 27, 2013

No it does not compile below Delphi 2009 because of its generics usage. The Delphi language needs these “evolutions” (generics, etc.) to compete with actual languages and it makes programming with Delphi a lot more structured. I don’t understand those people who are resistent to these technical achievements.

_Robert
March 27, 2013

Generics are fine for novice programmers who have difficulty type-casting. There is no “evolution” to compete with. I have never had a client request a project that delphi 7 couldn’t easily accomplish. I’m an old school ASM programmer, so I’m not impressed with how much prettier things get. I can replicate any project you can make in in D7, it will have less overhead, and an overall smaller size. I enjoy using what I know works.

Dennis
March 27, 2013

The executable is smaller but the source code is way bigger, thus more error prone and less managable (in my opinion). I’m in 2013 and I don’t care about a 30MB exe file instead of 10 MB.
I am an old school asm programmer as well, but I’ve moved forward since these days and enjoyed every new language feature in D2009+ (generics, records, rtti, etc.) – our team produced a way better source code since that…

Well, different persons different opinions ;-)

_Robert
March 27, 2013

True.
As much is I dislike .Net, I have been guilty of writing a few programs in Delphi 2010. Mostly charting applications. It’s refreshing to see someone enthusiastic about newer technologies. My clients usually like keeping things as cross platform as possible.

Been great talking with you Dennis. Good luck.

Leave a comment

About

I'm an avid programmer working on a variety of platforms in a variety of languages with a wide technical interest.

QR Code

Search

Categories