Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2015-01

Read/write records from/to XML

4 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Bart

Hi,

I have an XML file with the folowing structure:

<?xml version="1.0" encoding="utf-8"?>
<AppName>
<Info>
<Application>Versie 3.0.1</Application>
<CopyRight>(C) FooBar</CopyRight>
<Warning>A message in a bottle</Warning>
</Info>
<Persons Count=99>
<Person Index="000">
<Name>Jan</Name>
<City>Bergentheim</City>
<Email></Email>
</Person">
<Person Index="001">
<Name>Mary</Name>
<City>Amsterdam</City>
<Email></Email>
</Person>
....
</Persons>
</AppName>

What would be the best/most simple way to:
- sequentially read "Count" persons
- find person with Index=X and read Name, City etc?

Currently I iterate through all nodes from the TXMlDocument I read using ReadXMLFile() and parsing all Text-node values. It goes something like this:

    iNode := XMLDoc.FirstChild;
    while iNode  nil do
    begin
      ProcessNode(iNode,  0); // Recursive
      iNode := iNode.NextSibling;
    end;

In ProcessNode I check the nodetype, construct the path, check if the path is valid, determine if it is a name, city, some other field, or invalid and store the value in the appropriate recordfield.

This is rather cumbersome.
Mosty, because I need (want) to check that e.g. a City node must be a child of a Person node, which must be child of Persons node, which must be child the root node.

E.g. if someone text-edits the file (that's the downside of making it human-readable) and makes it like so:

...
<Persons Count=99>
<Name>Klaas</Name>
<Person Index="000">
<Name>Jan</Name>
<City>Bergentheim</City>
<Email></Email>
</Person">

The <Name>Klaas</Name> (whilst being legal xml) should be (silently) disregarded.

So, I'm thinking I'm not using the most optimal method reading the XML-file, certainly there must be suitable classes to do such a thing?

So, basically what I am looking for is, given the TXMlDocument object:
- find value of Persons/Count
- find Persons/Person with Index=X/Name

Preferrably I want a solution that does not drag in databse components (the program is supposed to be stand-alone, no external libraries should be needed).

Bart

Reply by email

Anthony Walter
to Bart

Have you considered xpath? It's a core feature of all xml engines (msxml, libxml2, whatever else).

Syntax overview:

http://msdn.microsoft.com/en-us/library/ms256086(v=vs.110).aspx

In my wrapper would look like this:

Person := Document.FindNode(' = "0001"]');

Other examples

// All persons in a city that start with the letter A
  Document.FindNodes('//Person[starts-with(City, "A")]');
// Or to processing save time, select the Persons node
  People := Document.FindNode('//Persons');
// And now xpath is evaluated relative to that
  People.FindNode('Person[Name = "Jan"]');

And so on
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20150111/e4819232/attachment.html>

Reply by email

Michael Van Canneyt
to Anthony Walter
Anthony Walter wrote 20 lines
Have you considered xpath? It's a core feature of all xml engines (msxml, libxml2, whatever else). Syntax overview: http://msdn.microsoft.com/en-us/library/ms256086(v=vs.110).aspx In my wrapper would look like this: Person := Document.FindNode(' = "0001"]'); Other examples // All persons in a city that start with the letter A ? Document.FindNodes('//Person[starts-with(City, "A")]'); // Or to processing save time, select the Persons node ? People := Document.FindNode('//Persons'); // And now xpath is evaluated relative to that ? People.FindNode('Person[Name = "Jan"]'); And so on

Free Pascal comes with the XPath unit, which implements the XPath spec. It does not drag in anything except the dom unit.

Michael.

Reply by email