XML Tutorial/ru

From Lazarus wiki

Deutsch (de) English (en) español (es) français (fr) magyar (hu) Bahasa Indonesia (id) italiano (it) 日本語 (ja) 한국어 (ko) português (pt) русский (ru) 中文(中国大陆)‎ (zh_CN)

XML - Расширяемый Язык Разметки (eXtensible Markup Language) рекомендован W3C как язык для обмена информацией между различными системами. Это ориентированный на текст способ сохранения информации. Современные языки обмена данными, такие как XHTML, так же как и большинство технологий WebServices, основаны на XML.

Это текстовый способ хранения информации, а не хранения информации в двоичном формате.

Современные языки обмена данными, такие как XHTML, а также большинство технологий WebServices, основаны на XML. Эта вики может действительно дать только краткий обзор XML, при этом основное внимание уделяется анализу и использованию файлов XML в приложениях Free Pascal. Если вас интересует более полное объяснение XML и его использования, см. здесь.

Введение

В настоящее время в Free Pascal существует ряд модулей для поддержки XML. Эти модули называются "XMLRead", "XMLWrite" и "DOM", и являются частью Free Component Library (FCL) из комплекта Free Pascal. FCL уже находится в заданном по умолчанию пути поиска файлов для компилятора в Лазарусе, таким образом Вам нужно только добавить названия модулей в строку USES чтобы получить поддержку XML в Вашей программе. Использование XML пока не документировано, поэтому данная статья даёт необходимые вводные сведения для работы с модулями поддержки XML.

DOM XML (Объектная модель документов) - это ряд стандартизированных объектов, которые предоставляют однотипный интерфейс для использования XML в различных языках и платформах. Стандарт определяет только методы, свойства и другие части интерфейса объекта, оставляя реализацию свободной для различных языков. FCL в настоящее время поддерживает полностью DOM 1.0.

Примеры

В статье даны примеры работы с XML-данными по принципу нарастающей сложности.

Использование: Юникод или Ansi

FPC предоставляет XML модули, использующие кодировку ANSI. Поэтому, они могут различаться в зависимости от платформы, в которой выполняется код и не иметь поддержку Юникода. Lazarus, в свою очередь, предоставляет собственный набор XML модулей, расположенных в пакете LazUtils. Они полностью поддерживают Юникод в формате UTF-8 и не зависят от выполняющей код платформы. Эти модули взаимозаменяемы и их использование определяется лишь наличием в списке uses.

Следующие модули FPC XML используют системную кодировку:

  • DOM
  • XMLRead
  • XMLWrite
  • XMLCfg
  • XMLUtils
  • XMLStreaming

Эти модули предоставлены Lazarus XML и имеют поддержку Юникода в формате UTF-8:

  • laz2_DOM
  • laz2_XMLRead
  • laz2_XMLWrite
  • laz2_XMLCfg
  • laz2_XMLUtils
  • laz_XMLStreaming.

Не все из них нужны в каждом примере. Однако, вам понадобится DOM, т.к. в нем содержится несколько типов, включая TXMLDocument.

Чтение текстового узла

Для Delphi-программистов:

Помните, что когда Вы работаете с TXMLDocument, текст в пределах узла считается отдельным текстовым узлом. Таким образом, Вы должны обратиться к текстовому значению узла, как к отдельному узлу. Альтернативно, свойство TextContent может быть использовано для возврата значения всех узлов лежащих ниже, которые связаны с данным.

Процедура ReadXMLFile всегда создаёт новый TXMLDocument, таким образом Вы не должны создавать его заранее. Однако Вы должны вызывать метод Free вручную после окончания работы с документом для освобождения ресурсов занятых объектом TXMLDocument.

Для примера рассмотрим следующий XML-файл:

 <?xml version="1.0"?>
 <request>
   <request_type>PUT_FILE</request_type>
   <username>123</username>
   <password>abc</password>
 </request>

Следующий пример показывает корректный и некорректный способы получения значений текстового узла xml:

 var
  PassNode: TDOMNode;
  Doc:      TXMLDocument;
 begin
  // Читаем xml файл с жесткого диска
  ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');
  try
    // Запрашиваем узел с именем "password"
    PassNode := Doc.DocumentElement.FindNode('password');
    // Выводим значение выбранного узла
    //Неправильный способ
    WriteLn(PassNode.NodeValue); // вывод будет пустым
    //Правильный способ
    // Текст узла - это отдельный дочерний узел
    WriteLn(PassNode.FirstChild.NodeValue); // правильно выведет "abc"
    // Альтернативный способ
    WriteLn(PassNode.TextContent);
  finally
    // В завершении делаем Free для документа
    Doc.Free;
  end; 
end;

Обратите внимание, что ReadXMLFile (...) игнорирует все начальные символы пробелов при анализе документа. Раздел символы пробелов описывает, как их сохранить.

Вывод имен узлов и атрибутов

Маленькое замечание о навигации по дереву DOM:

Для последовательного доступа к узлам лучше всего использовать свойства FirstChild и NextSibling (чтобы шагать вперед по дереву) или LastChild и PreviousSibling (назад с конца дерева). Для произвольного доступа к узлам дерева можно пользоваться функциями FindNode (ищется первый узел верхнего уровня с подходящим именем) или GetElementsByTagName (создается объект TDOMNodeList, который после использования должен быть освобождён). Это поведение отличается от других реализаций DOM (например, MSXML), поскольку FCL реализация основана на объектах, а не на интерфейсах.


Прим.перев: кроме того, существует функция GetNamedItem для поиска аттрибута по имени в текущем узле. Упрощенный пример (оригинал кода здесь):

var 
  aNode, AttrNode: TDOMNode;
begin
...
 //в текущем узле aNode пытаемся найти аттрибут цвета с именем 'color' и присвоить его узлу AttrNode
 AttrNode:= aNode.Attributes.GetNamedItem('color');
 //если такой узел создан (и не равен nil)
 if Assigned(AttrNode) then
 begin
   //проверяем значение аттибута
   if AttrNode.NodeValue <> ''
   then
     //если значение задано, то считываем его и присваиваем свойству цвета Memo1
     Memo1.Color:= StringToColor(AttrNode.NodeValue)
   else
     Memo1.Color:= clInfoBk;
   end;
end;


Следующий пример демонстрирует, как выводить имена узлов в компонент TMemo, расположенный на форме.

Ниже приведён XML-файл с именем 'C:\Programs\test.xml':

 <?xml version="1.0"?>
 <images directory="mydir">
  <imageNode URL="graphic.jpg" title="">
    <Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca>
    <Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca>
  </imageNode>
 </images>

И код на Pascal, который выполняет эту задачу:

var
  Doc: TXMLDocument;
  Child: TDOMNode;
  j: Integer;
begin
  try
    ReadXMLFile(Doc, 'test.xml');
    Memo.Lines.Clear;
    // Используем свойства FirstChild и NextSibling
    Child := Doc.DocumentElement.FirstChild;
    while Assigned(Child) do
    begin
      Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);
      // Используем свойство ChildNodes
      with Child.ChildNodes do
      try
        for j := 0 to (Count - 1) do
          Memo.Lines.Add(format('%s %s (%s=%s; %s=%s)',
                                [
                                  Item[j].NodeName,
                                  Item[j].FirstChild.NodeValue,
                                  Item[j].Attributes.Item[0].NodeName,  // 1-й атрибут
                                  Item[j].Attributes.Item[0].NodeValue,
                                  Item[j].Attributes.Item[1].NodeName,  // 2-й атрибут
                                  Item[j].Attributes.Item[1].NodeValue
                                ]));
      finally
        Free;
      end;
      Child := Child.NextSibling;
    end;
  finally
    Doc.Free;
  end;
end;

В результате программа выведет следующее:

imageNode graphic.jpg
Peca Pecacastelo.jpg1.swf (DestinoX=0; DestinoY=0)
Peca Pecacastelo.jpg2.swf (DestinoX=0; DestinoY=86)

Прим. перев.: Один из участников форума Werner Pamler (a.k.a. wp) предложил рекурсивную функцию поиска по всему дереву первого подходящего дочернего узла по имени, начиная с указанного родительского узла:

function FindNode(AParent: TDOMNode; AName: String): TDOMNode;
  var
    node: TDOMNode;
  begin
    Result := AParent.FindNode(AName);
    if Result = nil then begin
      node := AParent.FirstChild;
      while node <> nil do begin
        Result := FindNode(node, AName);
        if Result <> nil then
          exit;
        node := node.NextSibling;
      end;
    end;
  end;

Пример использования:

var aDoc: TXMLDocument;
    aChildNode: TDOMNode;
begin
  //будем считать, что aDoc уже заполнен данными
  ...
  //ищем дочерний узел с именем 'MyChildNode',
  //начиная с самого первого узла (aDoc.DocumentElement) документа aDoc
  aChildNode:= FindNode(aDoc.DocumentElement,'MyChildNode');
  if not Assigned(aNode) then
    showmessage('узла с именем "MyChildNode" не существует')
  else
    showmessage('узел с именем "MyChildNode" найден');
end;

Загрузка XML в TreeView

Одно из обычных использований файла XML - разбор и показ информации в древовидном формате. Вы можете отыскать компонент TTreeView на вкладке "Common Controls" Lazarus'а.

Функция, приведённая ниже, возмёт документ XML, предварительно загруженный из файла или сгенерированный программно, и заполнит TreeView его содержимым. Заголовком каждого узла будет содержимое первого атрибута этого узла.

procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);
var
  iNode: TDOMNode;

  procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);
  var
    cNode: TDOMNode;
    s: string;
  begin
    if Node = nil then Exit; // выходим, если достигнут конец документа
    
    // добавляем узел в дерево
    if Node.HasAttributes and (Node.Attributes.Length>0) then
      s:=Node.Attributes[0].NodeValue
    else
      s:=''; 
    TreeNode := tree.Items.AddChild(TreeNode, s);

    // переходим к дочернему узлу
    cNode := Node.FirstChild;

    // проходим по всем дочерним узлам
    while cNode <> nil do
    begin
      ProcessNode(cNode, TreeNode);
      cNode := cNode.NextSibling;
    end;
  end;
    
begin
  iNode := XMLDoc.DocumentElement.FirstChild;
  while iNode <> nil do
  begin
    ProcessNode(iNode, nil); // Рекурсия
    iNode := iNode.NextSibling;
  end;
end;

Другой пример, который отображает полную структуру XML, включая все значения атрибутов (примечание: длинная строка, ссылающаяся на TreeView, была разделена, поэтому она будет переносить слова для этой вики; при записи в коде вам не нужно разбивать строку, если вам не нравится форматирование ):

procedure XML2Tree(XMLDoc:TXMLDocument; TreeView:TTreeView);

  //Локальная функция, которая выводит все атрибуты узла в виде строки
  function GetNodeAttributesAsString(pNode: TDOMNode):string;
  var i: integer;
  begin
    Result:='';
    if pNode.HasAttributes then
      for i := 0 to pNode.Attributes.Length -1 do
        with pNode.Attributes[i] do
          Result := Result + format(' %s="%s"', [NodeName, NodeValue]);

    //Удаляем начальные и конечные пробелы
    Result:=Trim(Result);
  end;

  // Рекурсивная функция для обработки узла и всех его дочерних узлов 

  procedure ParseXML(Node:TDOMNode; TreeNode: TTreeNode);
  begin
    //Выход из процедуры, если больше нет узлов для обработки
    if Node = nil then Exit;

    //Добавляем узел в TreeView
    TreeNode := TreeView.Items.AddChild(TreeNode, 
                                          Trim(Node.NodeName+' '+ 
                                           GetNodeAttributesAsString(Node)+ 
                                           Node.NodeValue)
                                        );

    //Обрабатываем все дочерние узлы
    Node := Node.FirstChild;
    while Node <> Nil do
    begin
      ParseXML(Node, TreeNode);
      Node := Node.NextSibling;
    end;
  end;

begin
  TreeView.Items.Clear;
  ParseXML(XMLDoc.DocumentElement,nil);
end;

Изменение XML документа

Первая вещь, о которой следует помнить, TDOMDocument это хэндл (handle) к DOM. Вы можете получить экземпляр этого класса создавая или загружая XML документ.

С другой стороны, узлы не могут быть созданы как обычные объекты. Вы должны использовать методы, которые предоставляет TDOMDocument для их создания и, впоследствии, использовать другие методы для помещения их в нужное место дерева. Это говорит о том, что узлы должны принадлежать вполне определённому документу DOM.

Вот некоторые общераспространённые методы TDOMDocument:

   function CreateElement(const tagName: DOMString): TDOMElement; virtual;
   function CreateTextNode(const data: DOMString): TDOMText;
   function CreateCDATASection(const data: DOMString): TDOMCDATASection; virtual;
   function CreateAttribute(const name: DOMString): TDOMAttr; virtual;

CreateElement создает новый элемент.

CreateTextNode создает текстовый узел.

CreateAttribute создает атрибут узла.

CreateCDATASection создает раздел CDATA: обычные символы разметки XML, такие как <>, не интерпретируются в разделе CDATA. См. статью по CDATA на Wikipedia

Более удобный метод для управления атрибутами - использовать метод TDOMElement.SetAttribute, который также представлен как свойство по умолчанию TDOMElement:

// эти две конструкции эквивалентны
Element.SetAttribute('name', 'value');
Element['name'] := 'value';



Прим.перев.: вообще-то, автор оригинального текста немного ошибается, потому что выше приведенный код у вас просто не скомпилируется. Правильнее он выглядит так:

var Element: TDOMNode;
begin
...
// эти две конструкции эквивалентны
TDOMElement(Element).SetAttribute('name', 'value');
TDOMElement(Element).AttribStrings['name']:= 'value';
...
end;
Note-icon.png

Примечание: Имена аттрибутов узла не могут начинаться с цифры! Если имена аттрибутов у узла одинаковые, то записывается значение последнего указанного аттрибута

var Element: TDOMNode;
begin
...
TDOMElement(Element).SetAttribute('name', 'value 1');
TDOMElement(Element).SetAttribute('name', 'value 2');
//в файле мы увидим лишь <Element name="value 2">
...
end;


Вот, в качестве примера, метод, который ищет выбранный элемент в TTreeView и затем вставляет дочерний узел в документ XML, где он должен находиться . TreeView должен быть предварительно заполнен содержанием из XML файла, используя XML2Tree function.

procedure TForm1.actAddChildNode(Sender: TObject);
var
  position: Integer;
  NewNode: TDomNode;
begin
  {*******************************************************************
  *  Определение выбраного элемента
  *******************************************************************}
  if TreeView1.Selected = nil then Exit;

  if TreeView1.Selected.Level = 0 then
  begin
    position := TreeView1.Selected.Index;

    NewNode := XMLDoc.CreateElement('item');
    TDOMElement(NewNode).SetAttribute('nome', 'Item');
    TDOMElement(NewNode).SetAttribute('file', 'File');
    with XMLDoc.DocumentElement.ChildNodes do
    begin
      Item[position].AppendChild(NewNode);
      Free;
    end;

    {*******************************************************************
    *  Обновление TreeView
    *******************************************************************}
    TreeView1.Items.Clear;
    XML2Tree(TreeView1, XMLDoc);
  end
  else if TreeView1.Selected.Level >= 1 then
  begin
    {*******************************************************************
    *  Эта функция работает только на верхнем уровне дерева
    *  и вы должны её модифицировать, чтобы использовать другие уровни
    *******************************************************************}
  end;
end;

Создание TXMLDocument из строки

Если данные XML находятся в строке MyXmlString, создать из нее DOM можно с помощью следующего кода:

var
  S: TStringStream;
  XML: TXMLDocument;
begin
  S := TStringStream.Create('');
  try
    //Читаем XML документ полностью
    ReadXMLFile(XML, S);             
    //Альтернативно: чтение только фрагмента XML
    ReadXMLFragment(AParentNode, S); 
  finally
    S.Free;
  end;
end;

Проверка достоверности документа

Начиная с марта 2007, в FCL XML парсер добавлена возможность проверки достоверности (валидации) документа с помощью DTD. Проверка достоверности определяет соответствие логической структуры документа определенным правилам, заданных с помощью "Определение Типа документа" (DTD).

Это пример XML-документа, содержащего DTD:

  <?xml version='1.0'?>
  <!DOCTYPE root [
  <!ELEMENT root (child)+ >
  <!ELEMENT child (#PCDATA)>
  ]>
  <root>
    <child>This is a first child.</child>
    <child>And this is the second one.</child>
  </root>

Этот DTD определяет, что элемент root должен содержать один или более элемент child, а элементы child могут содержать только символьные данные. Если парсер обнаружит нарушение этих правил, то он сообщит о них.

Загрузка такого документа несколько сложнее. Пусть исходные XML данные содержатся в потоке AStream:

procedure TMyObject.DOMFromStream(AStream: TStream);
var
  Parser: TDOMParser;
  Src: TXMLInputSource;
  TheDoc: TXMLDocument;
begin
  try
    // Создаём объект-парсер
    Parser := TDOMParser.Create;
    // и источник данных
    Src := TXMLInputSource.Create(AStream);
    // Включаем проверку достоверности
    Parser.Options.Validate := True;
    // Назначаем обработчик ошибок, который будет получать уведомления о них
    Parser.OnError := @ErrorHandler;
    // А теперь работаем
    Parser.Parse(Src, TheDoc);
  finally
    // ... и убираем за собой :)
    Src.Free;
    Parser.Free;
  end;  
end;

procedure TMyObject.ErrorHandler(E: EXMLReadError);
begin
  if E.Severity = esError then  // Нас интересуют только ошибки проверки достоверности
    writeln(E.Message);
end;

Cимволы пробелов

Если необходимо сохранять пробельные символы в тексте узлов, следует пользоваться вышеприведённым методом загрузки XML документа. По умолчанию начальные пробельные символы игнорируются, именно поэтому функция ReadXML(...) пропускает все пробельные символы в начале текста узлов. Перед вызовом Parser.Parse(Src, TheDoc) добавьте следующую строку:

Parser.Options.PreserveWhitespace := True;

После этого парсер будет оставлять все пробельные символы, включая все переводы строк, добавленные для повышения читаемости XML документа!

Используйте следующий код, чтобы у вас была процедура ReadXMLFile, которая сохраняет первые пробелы:

procedure ReadXMLFilePreserveWhitespace(out Doc: TXMLDocument; FileName: string);
var
  Parser: TDOMParser;
  Src: TXMLInputSource;
  InFile: TFileStream;
begin
  try
    InFile := TFileStream.Create(FileName, fmOpenRead);
    Src := TXMLInputSource.Create(InFile);
    Parser := TDOMParser.Create;
    Parser.Options.PreserveWhitespace := True;
    Parser.Parse(Src, Doc);
  finally
    Src.Free;
    Parser.Free;
    InFile.Free;
  end;
end;

Потоковое чтение

Обработка XML с использованием DOM, требует загрузки всего документа в память. Это может быть не желательно или невозможно, если документ огромен. FCL предоставляет функции для чтения одного узла данных XML за один раз, используя класс TXMLReader и его потомков. Класс TXMLReader похож на класс XmlReader платформы .NET. Ниже приведен простой пример использования TXmlReader:

uses
  Classes,xmlreader,xmltextreader,xmlutils;

procedure readme(AStream: TStream);
var
  xtr: TXmlReader;
  settings: TXMLReaderSettings;
  inp: TXMLInputSource;
begin
  settings := TXMLReaderSettings.Create;
  try
    settings.PreserveWhiteSpace := True;
    settings.Namespaces := True;
    inp := TXMLInputSource.Create(AStream);
    try
      xtr := TXmlTextReader.Create(inp,settings);
      try
        // Здесь начинается чтение
        while xtr.Read do
        begin
          write(xtr.NodeType:25);
          if xtr.name<>'' then
            write(xtr.Name:9)
          else
            write('*no name* ');
          write(xtr.Value);
          writeln;
          if xtr.NodeType=ntElement then
          begin
            // вывод атрибутов
            if xtr.MoveToFirstAttribute then
            begin
              repeat
                writeln('---',xtr.NodeType:21,xtr.Name:10,xtr.Value:10);
              until not xtr.MoveToNextAttribute;
              xtr.MoveToContent;
            end;  
          end;
        end;
      // Очистка  
      finally
        xtr.Free;
      end;
    finally
      inp.Free;
    end;
  finally
    settings.Free;
  end;
end;

Генерация файла XML

Ниже приведён код для записи XML в файле. (Взято из обучающей программы в блоге DeveLazarus). Пожалуйста помните, что модули DOM и XMLWrite должны быть включены в Uses

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,
  DOM, XMLWrite;

type
  { TForm1 }
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;
  
var
  Form1: TForm1;
  
implementation

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  XDoc: TXMLDocument;                                  // переменная документа
  RootNode, ParentNode, ChildNode: TDOMNode;           // переменные узлов документа
begin
  //Создаём документ
  XDoc := TXMLDocument.create;

  //Создаём корневой узел
  RootNode := XDoc.CreateElement('register');
  XDoc.Appendchild(RootNode);                          // Добавляем корневой узел в документ

  RootNode:= XDoc.DocumentElement;                     //назначаем Root-узлом текущий узел
  ParentNode:= XDoc.CreateElement('user');             //создаём родительский узел
  TDOMElement(ParentNode).SetAttribute('id', '001');   // создаём атрибуты родительского узла
  RootNode.Appendchild(ParentNode);                    // добавляем родительский узел <user id="001"></user> внутри корневого <register></register>

  ParentNode:= XDoc.CreateElement('name');             //создаём дочерний по отношению к <user id="001"></user> узел
  //TDOMElement(ParentNode).SetAttribute('sex', 'M');   
  ChildNode:= XDoc.CreateTextNode('Fernando');         // вставляем значение в узел
  ParentNode.Appendchild(ChildNode);                   // сохраняем дочерний узел внутри родителя
  RootNode.ChildNodes.Item[0].AppendChild(ParentNode); // вставляем дочерний узел <name>Fernando</name> в соответствующий родительский узел 
                                                       // <user id="001"></user> (он будет в корневом узле RootNode первым - отсчет с нуля - RootNode.ChildNodes.Item[0])

  //Создаём ещё один дочерний узел (аналогично выше описанному)
  ParentNode := XDoc.CreateElement('age');               
  //TDOMElement(parentNode).SetAttribute('year', '1976');   
  ChildNode:= XDoc.CreateTextNode('32');                // вставляем значение в узел
  ParentNode.Appendchild(ChildNode);                    // сохраняем узел
  RootNode.ChildNodes.Item[0].AppendChild(ParentNode);  // вставляем дочерний узел в соответствующий родительский

  //создаем строку вида <!--это коммент--> внутри тега <user id="001"></user>
  ChildNode:= ADoc.CreateComment('это коммент');
  RootNode.ChildNodes.Item[0].AppendChild(ChildNode);

  //создаем строку вида <![CDATA[это CDATASection]]> внутри тега <user id="001"></user>
  ChildNode:= ADoc.CreateCDATASection('это CDATASection');
  RootNode.ChildNodes.Item[0].AppendChild(ChildNode);   

  writeXMLFile(XDoc,'test.xml');                          // записываем всё в XML-файл
  XDoc.free;                                               // освобождаем память
end;

initialization
  {$I unit1.lrs}

end.

Вот такой XML-файл у нас должен получится в результате:

<?xml version="1.0"?>
<register>
  <user id="001">
    <name>Fernando</name>
    <age>32</age>
    <!--это коммент-->
    <![CDATA[это CDATASection]]>
  </user>
</register>

Прим.переводчика: обратите внимание, что функция function CreateElement(const tagName: DOMString): TDOMElement; virtual; допускает в качестве агрумента строки, содержащие только цифры и буквы латиницы без пробелов, т.е. в выше приведенном примере попытка написать:

...
RootNode:= xdoc.CreateElement('my register');
//или RootNode:= xdoc.CreateElement('регданные');
//или RootNode:= xdoc.CreateElement('re.gister');
...

немедленно вызовет исключение "EDOMError in DOMDocument.CreateElement" с кодом ошибки INVALID_CHARACTER_ERR.

То же самое правило справедливо и для имен аттрибута/ов узла - см. мое замечание выше.


Пример, где вам не нужно ссылаться на элемент по индексу.

procedure TForm1.Button2Click(Sender: TObject);
var
  Doc: TXMLDocument;
  RootNode, ElementNode,ItemNode,TextNode: TDOMNode;
  i: integer;
begin
  try
    // создаем документ
    Doc := TXMLDocument.Create;
    // создаем корневой узел
    RootNode := Doc.CreateElement('Root');
    Doc.Appendchild(RootNode);
    RootNode:= Doc.DocumentElement;
    // создаем узлы
    for i := 1 to 20 do
    begin
      ElementNode:=Doc.CreateElement('Element');
      TDOMElement(ElementNode).SetAttribute('id', IntToStr(i));

      ItemNode:=Doc.CreateElement('Item1');
      TDOMElement(ItemNode).SetAttribute('Attr1', IntToStr(i));
      TDOMElement(ItemNode).SetAttribute('Attr2', IntToStr(i));
      TextNode:=Doc.CreateTextNode('Item1Value is '+IntToStr(i));
      ItemNode.AppendChild(TextNode);
      ElementNode.AppendChild(ItemNode);

      ItemNode:=Doc.CreateElement('Item2');
      TDOMElement(ItemNode).SetAttribute('Attr1', IntToStr(i));
      TDOMElement(ItemNode).SetAttribute('Attr2', IntToStr(i));
      TextNode:=Doc.CreateTextNode('Item2Value is '+IntToStr(i));
      ItemNode.AppendChild(TextNode);
      ElementNode.AppendChild(ItemNode);

      RootNode.AppendChild(ElementNode);
    end;
    // сохраняем XML
    WriteXMLFile(Doc,'TestXML_v2.xml');
  finally
    Doc.Free;
  end;

Сгенерированный XML:

<?xml version="1.0"?>
<Root>
  <Element id="1">
    <Item1 Attr1="1" Attr2="1">Item1Value is 1</Item1>
    <Item2 Attr1="1" Attr2="1">Item2Value is 1</Item2>
  </Element>
  <Element id="2">
    <Item1 Attr1="2" Attr2="2">Item1Value is 2</Item1>
    <Item2 Attr1="2" Attr2="2">Item2Value is 2</Item2>
  </Element>
  <Element id="3">
    <Item1 Attr1="3" Attr2="3">Item1Value is 3</Item1>
    <Item2 Attr1="3" Attr2="3">Item2Value is 3</Item2>
  </Element>
</Root>

Кодировки

Начинаясь с ревизии SVN 12582, XMLReader может читать данные в любой кодировке при условии использования внешнего декодера. Смотрите XML_Decoders/ru для более детальной информации.

Согласно спецификации XML, атрибут encoding в первой строке XML является необязательным в случае, если фактическая кодировка - UTF-8 или UTF-16 (что определяется наличием BOM). В версии Lazarus 0.9.26 TXMLDocument имеет свойство Encoding, но оно игнорируется. При этом процедура WriteXMLFile всегда использует кодировку UTF-8 и не генерирует атрибут encoding в первой строке файла XML.

Дополнительные ссылки

  • TXMLPropStorage использование TXMLPropStorage для сохранения настроек программы.

Смотрите также