System.Xml and Delphi.net


see also http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemXmlXmlDocumentClassctorTopic.asp

That example looks in Delphi like this:

unit xmlparser;

interface
uses
  System.Xml,
  System.Xml.Schema;

{**
  This class provides methods for parsing an XML document and assign the contents
  to a representation in memory.
*}
type TXMLParser=class
protected
  {** The xml file *}
  myDoc: XmlDocument;
  reader: XmlValidatingReader;
  txtReader: XmlTextReader;

....

  procedure ValidationCallback(sender: TObject; args: ValidationEventArgs);

end;

implementation
uses System.IO;

constructor TXMLParser.Create(filename: string);
var eventHandler: ValidationEventHandler;
begin
  inherited Create();
  if not System.IO.File.Exists(filename) then begin
    raise Exception.Create('file ' + filename + ' could not be found.');
  end;

  eventHandler := ValidationCallback;
  reader := nil;
  try

    txtReader := XmlTextReader.Create(filename);
    reader := XmlValidatingReader.Create(txtReader);
    reader.ValidationType := ValidationType.DTD;

    // Set a handler to handle validation errors.
    Include(reader.ValidationEventHandler, eventHandler);

    myDoc := XmlDocument.Create();
    myDoc.Load(reader);
  finally
    if ( reader <> nil ) then
    begin
      reader.Close();
    end;
  end;
end;

procedure TXMLParser.ValidationCallback(sender: TObject; args: ValidationEventArgs);
begin
 Console.WriteLine('Validation error loading xml file');
 Console.WriteLine(args.Message);
end;


further nice functions:

{** test the given node, if it is a comment or is empty; if it is empty, try the next nodes.
    @param cur2 the current node
    @return the current or the first next node with actual content
*}
function TXMLParser.nextNotBlank(const cur2: XmlNode): XmlNode;
var
    cur: XmlNode;
begin
    cur := cur2;
    while (true) do
    begin
        if (cur = nil) then
    begin
            result := cur;
      exit;
    end;
        if ((cur.NodeType = System.Xml.XmlNodeType.Text) and (Length(cur.toString()) = 0)) then
    begin
      cur := cur.NextSibling;
      continue;
    end;
        if (cur.name = 'comment') then
        begin
            cur := cur.NextSibling;
            continue;
        end;
        if (cur.name = '#comment') then
        begin
            cur := cur.NextSibling;
            continue;
        end;
        result := cur;
    exit;
    end;
    result := nil;
end;

{** get the next code that is a proper entity (i.e. no comment, not empty)
    @param cur the current node
    @return the next node with actual content
*}
function TXMLParser.getNextEntity(const cur: XmlNode): XmlNode;
begin
    if (cur = nil) then
  begin
        result := nil
  end
    else
  begin
      result := nextNotBlank(cur.NextSibling);
  end;
end;

{** retrieve the value of an attribute. Does prevent unnecessary exceptions, if the attribute is not existing
    @param cur the current node
    @param attrib the name of the attribute
    @return the value of the attribute, or an empty string if it is not existing
*}
function TXMLParser.getAttribute(cur: XmlNode; attrib: string): string;
var node: XmlNode;
begin
  result := '';
  node :=  cur.Attributes.GetNamedItem(attrib);
  if (node <> nil) then result := node.Value;
end;

{**
  removes line breaks and tabulators and trims spaces, even inside the string
  @param s the string that should be cleaned up
  @return the clean string, without line breaks, tabulators and groups of spaces
}
function TXMLParser.cleanString(const s: string): string;
// removes \n and \t and trims spaces, even inside the string
var
    p: Integer;
    s2: string;
    ch, prev_ch: char;
begin
  s2 := s.Trim();
  result := '';
    p := 1;
  prev_ch := chr(0);
  while (p <= Length(s2)) do
    begin
    ch := s2[p];
        if (ch = chr(9)) then
      ch := ' ';
        if (ch = chr(10)) then
        ch := ' ';
    if (not ((prev_ch = ' ') and (ch = ' '))) then
    begin
      result := result + ch;
    end;
    prev_ch := ch;
    inc(p);
    end;
end;