Difference between revisions of "XML Tutorial/ru"

From Lazarus wiki
Jump to navigationJump to search
 
(79 intermediate revisions by 14 users not shown)
Line 1: Line 1:
 
{{XML Tutorial}}
 
{{XML Tutorial}}
  
 +
XML - Расширяемый Язык Разметки (e'''X'''tensible '''M'''arkup '''L'''anguage) рекомендован [http://www.w3.org/ W3C] как язык для обмена информацией между различными системами. Это ориентированный на текст способ сохранения информации. Современные языки обмена данными, такие как XHTML, так же как и большинство технологий WebServices, основаны на XML.
 +
 +
Это текстовый способ хранения информации, а не хранения информации в двоичном формате.
 +
 +
Современные языки обмена данными, такие как XHTML, а также большинство технологий WebServices, основаны на XML. Эта вики может действительно дать только краткий обзор XML, при этом основное внимание уделяется анализу и использованию файлов XML в приложениях Free Pascal. Если вас интересует более полное объяснение XML и его использования, см. [https://ru.wikipedia.org/wiki/XML здесь].
 +
 +
__TOC__
 
== Введение ==
 
== Введение ==
 +
В настоящее время в Free Pascal существует ряд модулей для поддержки XML. Эти модули называются "XMLRead", "XMLWrite" и "DOM", и являются частью Free Component Library (FCL) из комплекта Free Pascal. FCL уже находится в заданном по умолчанию пути поиска файлов для компилятора в Лазарусе, таким образом Вам нужно только добавить названия модулей в строку USES чтобы получить поддержку XML в Вашей программе. Использование XML пока не документировано, поэтому данная статья даёт необходимые вводные сведения для работы с модулями поддержки XML.
 +
 +
DOM XML (Объектная модель документов) - это ряд стандартизированных объектов, которые предоставляют однотипный интерфейс для использования XML в различных языках и платформах. Стандарт определяет только методы, свойства и другие части интерфейса объекта, оставляя реализацию свободной для различных языков. FCL в настоящее время поддерживает полностью [http://ru.wikipedia.org/wiki/DOM DOM 1.0].
 +
 +
== Примеры ==
 +
 +
В статье даны примеры работы с XML-данными по принципу нарастающей сложности.
  
XML - Расширяемый Язык Разметки (eXtensible Markup Language) рекомендован [http://www.w3.org/ W3C] как язык для обмена информацией между различными системами. Это ориентированный на текст способ сохранения информации. Современные языки обмена данными, такие как XHTML, так же как и большинство технологий WebServices, основаны на XML.
+
=== Использование: Юникод или Ansi ===
  
В настоящее время существует ряд модулей для поддержки XML в Free Pascal. Эти модули называются "XMLRead", "XMLWrite" и "DOM" и являются частью Free Component Library (FCL) из компилятора Free Pascal. FCL уже находится в заданном по умолчанию пути поиска файлов для компилятора в Лазарусе, таким образом Вам нужно только добавить названия модулей в строку USES чтобы получить поддержку XML в Вашей программе. Использование XML пока не документировано, поэтому эта статья даёт необходимые вводные сведения для работы с модулями поддержки XML.
+
FPC предоставляет XML модули, использующие кодировку ANSI. Поэтому, они могут различаться в зависимости от платформы, в которой выполняется код и не иметь поддержку Юникода. Lazarus, в свою очередь, предоставляет собственный набор XML модулей, расположенных в пакете LazUtils. Они полностью поддерживают Юникод в формате UTF-8 и не зависят от выполняющей код платформы. Эти модули взаимозаменяемы и их использование определяется лишь наличием в списке uses.
  
DOM XML (Объектная модель документов) это ряд стандартизированных объектов, которые предоставляют однотипный интерфейс для использования XML в различных языках и платформах. Стандарт определяет только методы, свойства и другие части интерфейса объекта, оставляя реализацию свободной для различных языков. FCL в настоящее время поддерживает полностью [http://ru.wikipedia.org/wiki/DOM DOM 1.0].
+
Следующие модули FPC XML используют системную кодировку:
 +
* DOM
 +
* XMLRead
 +
* XMLWrite
 +
* XMLCfg
 +
* XMLUtils
 +
* XMLStreaming
  
== Примеры ==
+
Эти модули предоставлены Lazarus XML и имеют поддержку Юникода в формате UTF-8:
 +
* laz2_DOM
 +
* laz2_XMLRead
 +
* laz2_XMLWrite
 +
* laz2_XMLCfg
 +
* laz2_XMLUtils
 +
* laz_XMLStreaming.
  
Bellow there is a list of XML data manipulation examples with growing complexity.
+
Не все из них нужны в каждом примере. Однако, вам понадобится DOM, т.к. в нем содержится несколько типов, включая [https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/txmldocument.html TXMLDocument].
  
 
=== Чтение текстового узла ===
 
=== Чтение текстового узла ===
  
For Delphi Programmers:
+
Для Delphi-программистов:
Note that when working with TXMLDocument, the text within a Node is considered a separate TEXT NodeAs a result, you must access a node's text value as a separate node. Alternatively, the '''TextContent''' property may be used to retrieve content of all text nodes beneath the given one, concatenated together.
+
 
 +
Помните, что когда Вы работаете с TXMLDocument, текст в пределах узла считается '''отдельным текстовым узлом'''Таким образом, Вы должны обратиться к текстовому значению узла, как к отдельному узлу. Альтернативно, свойство '''TextContent''' может быть использовано для возврата значения всех узлов лежащих ниже, которые связаны с данным.
  
The '''ReadXMLFile''' procedure always creates a new '''TXMLDocument''', so you don't have to create it beforehand. However, be sure to destroy the document by calling '''Free''' when you are done.
+
Процедура [https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_xmlread/readxmlfile.html '''ReadXMLFile'''] всегда создаёт новый '''TXMLDocument''', таким образом Вы не должны создавать его заранее. Однако Вы должны вызывать метод '''Free''' вручную после окончания работы с документом для освобождения ресурсов занятых объектом '''TXMLDocument'''.
  
For instance, consider the following XML:
+
Для примера рассмотрим следующий XML-файл:
  
<xml>
+
<syntaxhighlight lang="xml">
 
  <?xml version="1.0"?>
 
  <?xml version="1.0"?>
 
  <request>
 
  <request>
Line 29: Line 56:
 
   <password>abc</password>
 
   <password>abc</password>
 
  </request>
 
  </request>
</xml>
+
</syntaxhighlight>
  
Следующий пример показывает оба: корректный и не корректный способы получения значений текстового узла xml:
+
Следующий пример показывает корректный и некорректный способы получения значений текстового узла xml:
  
<delphi>
+
<syntaxhighlight lang=pascal>
 
  var
 
  var
 
   PassNode: TDOMNode;
 
   PassNode: TDOMNode;
Line 40: Line 67:
 
   // Читаем xml файл с жесткого диска
 
   // Читаем xml файл с жесткого диска
 
   ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');
 
   ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');
   // Запрашиваем узел с именем "password"
+
   try
  PassNode := Doc.DocumentElement.FindNode('password');
+
    // Запрашиваем узел с именем "password"
  // Write out value of the selected node
+
    PassNode := Doc.DocumentElement.FindNode('password');
  WriteLn(PassNode.NodeValue); // вывод будет пустым
+
    // Выводим значение выбранного узла
  // The text of the node is actually a separate child node
+
    //Неправильный способ
  WriteLn(PassNode.FirstChild.NodeValue); // правильно выведет "abc"
+
    WriteLn(PassNode.NodeValue); // вывод будет пустым
  // альтернатива
+
    //Правильный способ
  WriteLn(PassNode.TextContent);
+
    // Текст узла - это отдельный дочерний узел
   // finally, free the document
+
    WriteLn(PassNode.FirstChild.NodeValue); // правильно выведет "abc"
  Doc.Free;
+
    // Альтернативный способ
 +
    WriteLn(PassNode.TextContent);
 +
   finally
 +
    // В завершении делаем Free для документа
 +
    Doc.Free;
 +
  end;  
 
end;
 
end;
</delphi>
+
</syntaxhighlight>
  
=== Вывод названий узлов ===
+
Обратите внимание, что ReadXMLFile (...) игнорирует все начальные символы пробелов при анализе документа. Раздел [[XML_Tutorial/ru#C.D0.B8.D0.BC.D0.B2.D0.BE.D0.BB.D1.8B_.D0.BF.D1.80.D0.BE.D0.B1.D0.B5.D0.BB.D0.BE.D0.B2|символы пробелов]] описывает, как их сохранить.
  
Маленькое замечание о навигации по дереву DOM: Когда вам нужнен последовательный доступ к узлам, лучшим решением будет использовать свойства '''FirstChild''' и '''NextSibling'''  (чтобы шагать вперед по дереву), или '''LastChild''' и '''PreviousSibling''' (назад с конца). Для свободного доступа к элементам дерева можно пользоваться методами '''ChildNodes''' или '''GetElementsByTagName''', но это создаст объект TDOMNodeList, который в конце его использования должен быть freed. Это отличается от других DOM реализаций, таких к примеру как MSXML, поскольку FCL реализация базируется на объектах а не на интерфейсе.
+
=== Вывод имен узлов и атрибутов ===
  
Следующий пример демонстрирует как выводить имена узлов в компонент TMemo расположенный на форме.
+
Маленькое замечание о навигации по дереву DOM:
  
Ниже XML файл с именем 'C:\Programs\test.xml':
+
Для последовательного доступа к узлам лучше всего использовать свойства [https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomnode.firstchild.html FirstChild] и [https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomnode.nextsibling.html NextSibling] (чтобы шагать вперед по дереву) или [https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomnode.lastchild.html LastChild] и [https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomnode.previoussibling.html PreviousSibling] (назад с конца дерева). Для произвольного доступа к узлам дерева можно пользоваться функциями [https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomnode.findnode.html FindNode] (ищется первый узел верхнего уровня с подходящим именем) или [https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomdocument.getelementsbytagname.html GetElementsByTagName] (создается объект [https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomnodelist.html TDOMNodeList], который после использования должен быть освобождён). Это поведение отличается от других реализаций DOM (например, MSXML), поскольку FCL реализация основана на объектах, а не на интерфейсах.
  
<xml>
+
----
 +
[[user:zoltanleo| Прим.перев]]: кроме того, существует функция [https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomnamednodemap.getnameditem.html GetNamedItem] для поиска аттрибута по имени в текущем узле. Упрощенный пример (оригинал кода [https://forum.lazarus.freepascal.org/index.php/topic,50104.msg365179.html#msg365179 здесь]):
 +
<syntaxhighlight lang=pascal>
 +
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;
 +
</syntaxhighlight>
 +
----
 +
 
 +
 
 +
Следующий пример демонстрирует, как выводить имена узлов в компонент TMemo, расположенный на форме.
 +
 
 +
Ниже приведён XML-файл с именем 'C:\Programs\test.xml':
 +
 
 +
<syntaxhighlight lang="xml">
 
  <?xml version="1.0"?>
 
  <?xml version="1.0"?>
 
  <images directory="mydir">
 
  <images directory="mydir">
Line 69: Line 130:
 
   </imageNode>
 
   </imageNode>
 
  </images>
 
  </images>
</xml>
+
</syntaxhighlight>
  
And here the Pascal code to execute the task:
+
И код на Pascal, который выполняет эту задачу:
  
<delphi>
+
<syntaxhighlight lang=pascal>
var
+
var
  Documento: TXMLDocument;
+
  Doc: TXMLDocument;
  Child: TDOMNode;
+
  Child: TDOMNode;
  j: Integer;
+
  j: Integer;
begin
+
begin
  ReadXMLFile(Documento, 'C:\Programas\teste.xml');
+
  try
  Memo.Lines.Clear;
+
    ReadXMLFile(Doc, 'test.xml');
  // using FirstChild and NextSibling properties
+
    Memo.Lines.Clear;
  Child := Documento.DocumentElement.FirstChild;
+
    // Используем свойства FirstChild и NextSibling
  while Assigned(Child) do
+
    Child := Doc.DocumentElement.FirstChild;
  begin
+
    while Assigned(Child) do
    Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);
+
    begin
    // using ChildNodes method
+
      Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);
    with Child.ChildNodes do
+
      // Используем свойство ChildNodes
    try
+
      with Child.ChildNodes do
      for j := 0 to (Count - 1) do
+
      try
        Memo.Lines.Add(Item[j].NodeName + ' ' + Item[j].FirstChild.NodeValue);
+
        for j := 0 to (Count - 1) do
    finally
+
          Memo.Lines.Add(format('%s %s (%s=%s; %s=%s)',
      Free;
+
                                [
    end;
+
                                  Item[j].NodeName,
    Child := Child.NextSibling;
+
                                  Item[j].FirstChild.NodeValue,
  end;
+
                                  Item[j].Attributes.Item[0].NodeName,  // 1-й атрибут
  Documento.Free;
+
                                  Item[j].Attributes.Item[0].NodeValue,
end;
+
                                  Item[j].Attributes.Item[1].NodeName,  // 2-й атрибут
</delphi>
+
                                  Item[j].Attributes.Item[1].NodeValue
 +
                                ]));
 +
      finally
 +
        Free;
 +
      end;
 +
      Child := Child.NextSibling;
 +
    end;
 +
  finally
 +
    Doc.Free;
 +
  end;
 +
end;
 +
</syntaxhighlight>
  
В результате программа выведет следущее:
+
В результате программа выведет следующее:
  
<pre>
+
<pre>imageNode graphic.jpg
imageNode graphic.jpg
+
Peca Pecacastelo.jpg1.swf (DestinoX=0; DestinoY=0)
Peca Pecacastelo.jpg1.swf
+
Peca Pecacastelo.jpg2.swf (DestinoX=0; DestinoY=86)
Peca Pecacastelo.jpg1.swf
 
 
</pre>
 
</pre>
 +
 +
----
 +
[[user:zoltanleo|Прим. перев.]]: Один из участников форума Werner Pamler (a.k.a. [https://forum.lazarus.freepascal.org/index.php?action=profile;u=45589 wp]) [https://forum.lazarus.freepascal.org/index.php/topic,39432.msg270619.html#msg270619 предложил] рекурсивную функцию поиска по всему дереву <i>первого подходящего</i> дочернего узла по имени, начиная с указанного родительского узла:
 +
<syntaxhighlight lang=pascal>
 +
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;
 +
</syntaxhighlight>
 +
 +
Пример использования:
 +
<syntaxhighlight lang=pascal>
 +
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;
 +
</syntaxhighlight>
  
 
=== Загрузка XML в TreeView ===
 
=== Загрузка XML в TreeView ===
  
One common use of XML files is to parse them and show their contents in a tree like format. You can find the TTreeView component on the "Common Controls" tab on Lazarus.
+
Одно из обычных использований файла XML - разбор и показ информации в древовидном формате. Вы можете отыскать компонент TTreeView на вкладке "Common Controls" Lazarus.
  
The function below will take a XML document previously loaded from a file or generated on code, and will populate a TreeView with it´s contents. The caption of each node will be the content of the first attribute of each node.
+
Функция, приведённая ниже, возмёт документ XML, предварительно загруженный из файла или сгенерированный программно, и заполнит TreeView его содержимым. Заголовком каждого узла будет содержимое первого атрибута этого узла.
  
<delphi>
+
<syntaxhighlight lang=pascal>
 
procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);
 
procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);
 
var
 
var
Line 124: Line 232:
 
     s: string;
 
     s: string;
 
   begin
 
   begin
     if Node = nil then Exit; // Stops if reached a leaf
+
     if Node = nil then Exit; // выходим, если достигнут конец документа
 
      
 
      
     // Adds a node to the tree
+
     // добавляем узел в дерево
 
     if Node.HasAttributes and (Node.Attributes.Length>0) then
 
     if Node.HasAttributes and (Node.Attributes.Length>0) then
 
       s:=Node.Attributes[0].NodeValue
 
       s:=Node.Attributes[0].NodeValue
Line 133: Line 241:
 
     TreeNode := tree.Items.AddChild(TreeNode, s);
 
     TreeNode := tree.Items.AddChild(TreeNode, s);
  
     // Goes to the child node
+
     // переходим к дочернему узлу
 
     cNode := Node.FirstChild;
 
     cNode := Node.FirstChild;
  
     // Processes all child nodes
+
     // проходим по всем дочерним узлам
 
     while cNode <> nil do
 
     while cNode <> nil do
 
     begin
 
     begin
Line 148: Line 256:
 
   while iNode <> nil do
 
   while iNode <> nil do
 
   begin
 
   begin
     ProcessNode(iNode, nil); // Recursive
+
     ProcessNode(iNode, nil); // Рекурсия
 
     iNode := iNode.NextSibling;
 
     iNode := iNode.NextSibling;
 
   end;
 
   end;
 
end;
 
end;
</delphi>
+
</syntaxhighlight>
 +
 
 +
Другой пример, который отображает полную структуру XML, включая все значения атрибутов (примечание: длинная строка, ссылающаяся на TreeView, была разделена, поэтому она будет переносить слова для этой вики; при записи в коде вам не нужно разбивать строку, если вам не нравится форматирование ):
 +
 
 +
<syntaxhighlight lang=pascal>
 +
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;
 +
</syntaxhighlight>
  
 
=== Изменение XML документа ===
 
=== Изменение XML документа ===
  
The first thing to remember is that TDOMDocument is the "handle" to the DOM. You can get an instance of this class by creating one or by loading a XML document.
+
Первая вещь, о которой следует помнить, TDOMDocument это хэндл (handle) к DOM. Вы можете получить экземпляр этого класса создавая или загружая XML документ.
  
Nodes on the other hand cannot be created like a normal object. You *must* use the methods provided by TDOMDocument to create them, and latter use other methods to put them on the correct place on the tree. This is because nodes must be "owned" by a specific document on DOM.
+
С другой стороны, узлы не могут быть созданы как обычные объекты. Вы должны использовать методы, которые предоставляет TDOMDocument для их создания и, впоследствии, использовать другие методы для помещения их в нужное место дерева. Это говорит о том, что узлы должны принадлежать вполне определённому документу DOM.
  
Below are some common methods from TDOMDocument:
+
Вот некоторые общераспространённые методы TDOMDocument:
  
<delphi>
+
<syntaxhighlight lang=pascal>
 
   function CreateElement(const tagName: DOMString): TDOMElement; virtual;
 
   function CreateElement(const tagName: DOMString): TDOMElement; virtual;
 
   function CreateTextNode(const data: DOMString): TDOMText;
 
   function CreateTextNode(const data: DOMString): TDOMText;
   function CreateCDATASection(const data: DOMString): TDOMCDATASection;
+
   function CreateCDATASection(const data: DOMString): TDOMCDATASection; virtual;
    virtual;
 
 
   function CreateAttribute(const name: DOMString): TDOMAttr; virtual;
 
   function CreateAttribute(const name: DOMString): TDOMAttr; virtual;
</delphi>
+
</syntaxhighlight>
 +
 
 +
<tt>[https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomdocument.createelement.html CreateElement]</tt> создает новый элемент.
 +
 
 +
<tt>[https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomdocument.createtextnode.html CreateTextNode]</tt> создает текстовый узел.
 +
 
 +
<tt>[https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomdocument.createattribute.html CreateAttribute]</tt> создает атрибут узла.
 +
 
 +
<tt>[https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomdocument.createcdatasection.html CreateCDATASection]</tt> создает раздел CDATA: обычные символы разметки XML, такие как <>, не интерпретируются в разделе CDATA. См. [https://ru.wikipedia.org/wiki/ статью по CDATA на Wikipedia]
 +
 
 +
Более удобный метод для управления атрибутами - использовать метод <tt>TDOMElement.SetAttribute</tt>, который также представлен как свойство по умолчанию <tt>TDOMElement</tt>:
 +
 
 +
<syntaxhighlight lang=pascal>
 +
// эти две конструкции эквивалентны
 +
Element.SetAttribute('name', 'value');
 +
Element['name'] := 'value';
 +
</syntaxhighlight>
 +
 
 +
 
 +
----
 +
[[user:zoltanleo|Прим.перев.]]: вообще-то, автор оригинального текста немного ошибается, потому что выше приведенный код у вас просто не скомпилируется. Правильнее он выглядит так:
 +
<syntaxhighlight lang=pascal>
 +
var Element: TDOMNode;
 +
begin
 +
...
 +
// эти две конструкции эквивалентны
 +
TDOMElement(Element).SetAttribute('name', 'value');
 +
TDOMElement(Element).AttribStrings['name']:= 'value';
 +
...
 +
end;
 +
</syntaxhighlight>
 +
 
 +
{{Note| Имена аттрибутов узла не могут начинаться с цифры! Если имена аттрибутов у узла одинаковые, то записывается значение последнего указанного аттрибута}}
 +
<syntaxhighlight lang=pascal>
 +
var Element: TDOMNode;
 +
begin
 +
...
 +
TDOMElement(Element).SetAttribute('name', 'value 1');
 +
TDOMElement(Element).SetAttribute('name', 'value 2');
 +
//в файле мы увидим лишь <Element name="value 2">
 +
...
 +
end;
 +
</syntaxhighlight>
 +
----
  
And here an example method that will locate the selected item on a TTreeView and then insert a child node to the XML document it represents. The TreeView must be previously filled with the contents of a XML file using the [[Networking#Populating a TreeView with XML|XML2Tree function]].
 
  
<delphi>
+
Вот, в качестве примера, метод, который ищет выбранный элемент в TTreeView и затем вставляет дочерний узел в документ XML, где он должен находиться . TreeView должен быть предварительно заполнен содержанием из XML файла, используя [[XML_Tutorial/ru#.D0.97.D0.B0.D0.B3.D1.80.D1.83.D0.B7.D0.BA.D0.B0_XML_.D0.B2_TreeView|XML2Tree function]].
 +
 
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.actAddChildNode(Sender: TObject);
 
procedure TForm1.actAddChildNode(Sender: TObject);
 
var
 
var
 
   position: Integer;
 
   position: Integer;
   NovoNo: TDomNode;
+
   NewNode: TDomNode;
 
begin
 
begin
 
   {*******************************************************************
 
   {*******************************************************************
   *  Detects the selected element
+
   *  Определение выбраного элемента
 
   *******************************************************************}
 
   *******************************************************************}
 
   if TreeView1.Selected = nil then Exit;
 
   if TreeView1.Selected = nil then Exit;
Line 187: Line 386:
 
     position := TreeView1.Selected.Index;
 
     position := TreeView1.Selected.Index;
  
     NovoNo := XMLDoc.CreateElement('item');
+
     NewNode := XMLDoc.CreateElement('item');
     TDOMElement(NovoNo).SetAttribute('nome', 'Item');
+
     TDOMElement(NewNode).SetAttribute('nome', 'Item');
     TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo');
+
     TDOMElement(NewNode).SetAttribute('file', 'File');
 
     with XMLDoc.DocumentElement.ChildNodes do
 
     with XMLDoc.DocumentElement.ChildNodes do
 
     begin
 
     begin
       Item[position].AppendChild(NovoNo);
+
       Item[position].AppendChild(NewNode);
 
       Free;
 
       Free;
 
     end;
 
     end;
  
 
     {*******************************************************************
 
     {*******************************************************************
     *  Updates the TreeView
+
     *  Обновление TreeView
 
     *******************************************************************}
 
     *******************************************************************}
 
     TreeView1.Items.Clear;
 
     TreeView1.Items.Clear;
Line 205: Line 404:
 
   begin
 
   begin
 
     {*******************************************************************
 
     {*******************************************************************
     *  This function only works on the first level of the tree,
+
     *  Эта функция работает только на верхнем уровне дерева
     *  but can easely modifyed to work for any number of levels
+
     *  и вы должны её модифицировать, чтобы использовать другие уровни
 
     *******************************************************************}
 
     *******************************************************************}
 
   end;
 
   end;
 
end;
 
end;
</delphi>
+
</syntaxhighlight>
  
=== Создание TXMLDocument из обычного текста ===
+
=== Создание TXMLDocument из строки ===
  
Given al XML file in MyXmlString, the following code will create it's DOM:
+
Если данные XML находятся в строке MyXmlString, создать из нее DOM можно с помощью следующего кода:
 
 
<delphi>
 
Var
 
  S : TStringStream;
 
  XML : TXMLDocument;
 
  
 +
<syntaxhighlight lang=pascal>
 +
var
 +
  S: TStringStream;
 +
  XML: TXMLDocument;
 
begin
 
begin
   S:= TStringStream.Create(MyXMLString);
+
   S := TStringStream.Create('');
   Try
+
   try
    S.Position:=0;
+
     //Читаем XML документ полностью
     XML:=Nil;
+
     ReadXMLFile(XML, S);            
     ReadXMLFile(XML,S); // Complete XML document
+
     //Альтернативно: чтение только фрагмента XML
     // Alternatively:
+
     ReadXMLFragment(AParentNode, S);  
     ReadXMLFragment(AParentNode,S); // Read only XML fragment.
+
   finally
   Finally
 
 
     S.Free;
 
     S.Free;
 
   end;
 
   end;
 
end;
 
end;
</delphi>
+
</syntaxhighlight>
  
 
=== Проверка достоверности документа ===
 
=== Проверка достоверности документа ===
  
Since March 2007, DTD validation facility has been added to the FCL XML parser. Validation is checking that logical structure of the document conforms to the predefined rules, called ''Document Type Definition'' (DTD).
+
Начиная с марта 2007, в FCL XML парсер добавлена возможность проверки достоверности (валидации) документа с помощью DTD. Проверка достоверности определяет соответствие логической структуры документа определенным правилам, заданных с помощью "Определение Типа документа" (DTD).
  
Here is an example of XML document with a DTD:
+
Это пример XML-документа, содержащего DTD:
  
<xml>
+
<syntaxhighlight lang="xml">
 
   <?xml version='1.0'?>
 
   <?xml version='1.0'?>
 
   <!DOCTYPE root [
 
   <!DOCTYPE root [
Line 251: Line 448:
 
     <child>And this is the second one.</child>
 
     <child>And this is the second one.</child>
 
   </root>
 
   </root>
</xml>
+
</syntaxhighlight>
  
This DTD specifies that 'root' element must have one or more 'child' elements, and that 'child' elements may have only character data inside. If parser detects any violations from these rules, it will report them.
+
Этот DTD определяет, что элемент <code>root</code> должен содержать один или более элемент <code>child</code>, а элементы <code>child</code> могут содержать только символьные данные. Если парсер обнаружит нарушение этих правил, то он сообщит о них.
  
Loading such document is slightly more complicated. Let's assume we have XML data in a TStream object:
+
Загрузка такого документа несколько сложнее. Пусть исходные XML данные содержатся в потоке AStream:
  
<delphi>
+
<syntaxhighlight lang=pascal>
 
procedure TMyObject.DOMFromStream(AStream: TStream);
 
procedure TMyObject.DOMFromStream(AStream: TStream);
 
var
 
var
Line 264: Line 461:
 
   TheDoc: TXMLDocument;
 
   TheDoc: TXMLDocument;
 
begin
 
begin
   // create a parser object
+
   try
  Parser := TDOMParser.Create;
+
    // Создаём объект-парсер
  // and the input source
+
    Parser := TDOMParser.Create;
  Src := TXMLInputSource.Create(AStream);
+
    // и источник данных
  // we want validation
+
    Src := TXMLInputSource.Create(AStream);
  Parser.Options.Validate := True;
+
    // Включаем проверку достоверности
  // assign a error handler which will receive notifications
+
    Parser.Options.Validate := True;
  Parser.OnError := @ErrorHandler;
+
    // Назначаем обработчик ошибок, который будет получать уведомления о них
  // now do the job
+
    Parser.OnError := @ErrorHandler;
  Parser.Parse(Src, TheDoc);
+
    // А теперь работаем
   // ...and cleanup
+
    Parser.Parse(Src, TheDoc);
  Src.Free;
+
   finally
  Parser.Free;
+
    // ... и убираем за собой :)
 +
    Src.Free;
 +
    Parser.Free;
 +
  end; 
 
end;
 
end;
  
 
procedure TMyObject.ErrorHandler(E: EXMLReadError);
 
procedure TMyObject.ErrorHandler(E: EXMLReadError);
 
begin
 
begin
   if E.Severity = esError then  // we are interested in validation errors only
+
   if E.Severity = esError then  // Нас интересуют только ошибки проверки достоверности
 
     writeln(E.Message);
 
     writeln(E.Message);
 
end;
 
end;
</delphi>
+
</syntaxhighlight>
 +
 
 +
=== Cимволы пробелов ===
 +
Если необходимо сохранять пробельные символы в тексте узлов, следует пользоваться вышеприведённым методом загрузки XML документа. По умолчанию начальные пробельные символы игнорируются, именно поэтому функция ReadXML(...) пропускает все пробельные символы в начале текста узлов.
 +
Перед вызовом ''Parser.Parse(Src, TheDoc)'' добавьте следующую строку:
 +
 
 +
<syntaxhighlight lang=pascal>Parser.Options.PreserveWhitespace := True;</syntaxhighlight>
 +
 
 +
После этого парсер будет оставлять все пробельные символы, включая все переводы строк, добавленные для повышения читаемости XML документа!
 +
 
 +
Используйте следующий код, чтобы у вас была процедура ReadXMLFile, которая сохраняет первые пробелы:
 +
 
 +
<syntaxhighlight lang=pascal>
 +
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;
 +
</syntaxhighlight>
 +
 
 +
=== Потоковое чтение ===
 +
 
 +
Обработка XML с использованием DOM, требует загрузки всего документа в память. Это может быть не желательно или невозможно, если документ огромен. FCL предоставляет функции для чтения одного узла данных XML за один раз, используя класс TXMLReader и его потомков. Класс TXMLReader похож на класс XmlReader платформы .NET. Ниже приведен простой пример использования TXmlReader:
 +
 
 +
<syntaxhighlight lang=pascal>
 +
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;
 +
</syntaxhighlight>
  
 
=== Генерация файла XML ===
 
=== Генерация файла XML ===
  
Below is the complete code to write in a XML file.
+
Ниже приведён код для записи XML в файле.
(This was taken from a tutorial in DeveLazarus blog )
+
(Взято из обучающей программы в блоге DeveLazarus).
Please, remember DOM and XMLWrite libs in uses clause
+
Пожалуйста помните, что модули DOM и XMLWrite должны быть включены в Uses ([[user:zoltanleo|прим.перев.]] в примерах ниже для сохранениея XML-документа в файл используется перегруженная(overload) процедура [https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_xmlwrite/writexmlfile.html WriteXMLFile])
  
<delphi>
+
<syntaxhighlight lang=pascal>
 
unit Unit1;
 
unit Unit1;
  
Line 325: Line 613:
 
procedure TForm1.Button1Click(Sender: TObject);
 
procedure TForm1.Button1Click(Sender: TObject);
 
var
 
var
   xdoc: TXMLDocument;                                  // variable to document
+
   XDoc: TXMLDocument;                                  // переменная документа
   RootNode, parentNode, nofilho: TDOMNode;                   // variable to nodes
+
   RootNode, ParentNode, ChildNode: TDOMNode;           // переменные узлов документа
 
begin
 
begin
   //create a document
+
   //Создаём документ
   xdoc := TXMLDocument.create;
+
   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>
  
   //create a root node
+
  ParentNode:= XDoc.CreateElement('name');            //создаём дочерний по отношению к <user id="001"></user> узел
   RootNode := xdoc.CreateElement('register');
+
   //TDOMElement(ParentNode).SetAttribute('sex', 'M'); 
   Xdoc.Appendchild(RootNode);                           // save root node
+
   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])
  
   //create a parent node
+
   //Создаём ещё один дочерний узел (аналогично выше описанному)
  RootNode:= xdoc.DocumentElement;
+
   ParentNode := XDoc.CreateElement('age');              
   parentNode := xdoc.CreateElement('usuario');
+
   //TDOMElement(parentNode).SetAttribute('year', '1976'); 
   TDOMElement(parentNode).SetAttribute('id', '001');       // create atributes to parent node
+
  ChildNode:= XDoc.CreateTextNode('32');               // вставляем значение в узел
   RootNode.Appendchild(parentNode);                         // save parent node
+
  ParentNode.Appendchild(ChildNode);                    // сохраняем узел
 +
   RootNode.ChildNodes.Item[0].AppendChild(ParentNode); // вставляем дочерний узел в соответствующий родительский
  
   //create a child node
+
   //создаем строку вида <!--это коммент--> внутри тега <user id="001"></user>
  parentNode := xdoc.CreateElement('nome');                // create a child node
+
   ChildNode:= ADoc.CreateComment('это коммент');
  //TDOMElement(parentNode).SetAttribute('sexo', 'M');    // create atributes
+
   RootNode.ChildNodes.Item[0].AppendChild(ChildNode);
   nofilho := xdoc.CreateTextNode('Fernando');         // insert a value to node
 
  parentNode.Appendchild(nofilho);                        // save node
 
   RootNode.ChildNodes.Item[0].AppendChild(parentNode);       // insert child node in respective parent node
 
  
   //create a child node
+
   //создаем строку вида <![CDATA[это CDATASection]]> внутри тега <user id="001"></user>
  parentNode := xdoc.CreateElement('idade');              // create a child node
+
   ChildNode:= ADoc.CreateCDATASection('это CDATASection');
  //TDOMElement(parentNode).SetAttribute('ano', '1976');  // create atributes
+
   RootNode.ChildNodes.Item[0].AppendChild(ChildNode);  
   nofilho := xdoc.CreateTextNode('32');               // insert a value to node
 
  parentNode.Appendchild(nofilho);                        // save node
 
   .ChildNodes.Item[0].AppendChild(parentNode);       // insert a childnode in respective parent node
 
  
   writeXMLFile(xDoc,'teste.xml');                     // write to XML
+
   writeXMLFile(XDoc,'test.xml');                         // записываем всё в XML-файл
   Xdoc.free;                                         // free memory
+
   XDoc.free;                                               // освобождаем память
 
end;
 
end;
  
Line 363: Line 658:
  
 
end.
 
end.
</delphi>
+
</syntaxhighlight>
 
+
Вот такой XML-файл у нас должен получится в результате:
The  result will be the XML file below:
+
<syntaxhighlight lang="xml">
<xml>
 
 
<?xml version="1.0"?>
 
<?xml version="1.0"?>
 
<register>
 
<register>
   <usuario id="001">
+
   <user id="001">
     <nome>Fernando</nome>
+
     <name>Fernando</name>
     <idade>32</idade>
+
     <age>32</age>
   </usuario>
+
    <!--это коммент-->
 +
    <![CDATA[это CDATASection]]>
 +
   </user>
 
</register>
 
</register>
</xml>
+
</syntaxhighlight>
  
--[[User:Fernandosinesio|Fernandosinesio]] 22:28, 24 April 2008 (CEST)fernandosinesio@gmail.com
+
----
 +
[[User:Zoltanleo|Прим.переводчика]]: обратите внимание, что функция <code>function CreateElement(const tagName: DOMString): TDOMElement; virtual;</code> допускает в качестве агрумента строки, содержащие '''только цифры и буквы латиницы без пробелов''', т.е. в выше приведенном примере попытка написать:
 +
<syntaxhighlight lang=pascal>
 +
...
 +
RootNode:= xdoc.CreateElement('my register');
 +
//или RootNode:= xdoc.CreateElement('регданные');
 +
//или RootNode:= xdoc.CreateElement('re.gister');
 +
...
 +
</syntaxhighlight>
 +
 
 +
немедленно вызовет исключение "EDOMError in DOMDocument.CreateElement" с кодом ошибки INVALID_CHARACTER_ERR.
 +
 
 +
То же самое правило справедливо и для имен аттрибута/ов узла - см. мое замечание [[XML_Tutorial/ru#.D0.98.D0.B7.D0.BC.D0.B5.D0.BD.D0.B5.D0.BD.D0.B8.D0.B5_XML_.D0.B4.D0.BE.D0.BA.D1.83.D0.BC.D0.B5.D0.BD.D1.82.D0.B0|выше]].
 +
----
 +
 
 +
Пример, где вам не нужно ссылаться на элемент по индексу.
 +
 
 +
<syntaxhighlight lang=pascal>
 +
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;
 +
</syntaxhighlight>
 +
 
 +
Сгенерированный XML:
 +
 
 +
<syntaxhighlight lang="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>
 +
</syntaxhighlight>
  
 
=== Кодировки ===
 
=== Кодировки ===
  
Starting from SVN revision 12582, XML reader is able to process data in any encoding by using external decoders. See [[XML_Decoders]] for more details.
+
Начинаясь с ревизии SVN 12582, XMLReader может читать данные в любой кодировке при условии использования внешнего декодера. Смотрите [[XML_Decoders/ru]] для более детальной информации.
  
According to the XML standard, the encoding attribute in the first line of the XML is optional in case the actual encoding is UTF-8 or UTF-16 (which is detected by presence of the BOM). As of version 0.9.26 of Lazarus, there is an encoding property in a TXMLDocument, but it is ignored. writeXMLFile always uses UTF-8 and doesn´t generate an encoding attribute in first line of the XML file.
+
Согласно спецификации XML, атрибут encoding в первой строке XML является необязательным в случае, если фактическая кодировка - UTF-8 или UTF-16 (что определяется наличием BOM). В версии Lazarus 0.9.26 TXMLDocument имеет свойство Encoding, но оно игнорируется. При этом процедура WriteXMLFile всегда использует кодировку UTF-8 и не генерирует атрибут encoding в первой строке файла XML.
  
 
== Дополнительные ссылки ==
 
== Дополнительные ссылки ==
  
* [http://www.w3schools.com/xml/default.asp W3Schools] Xml Tutorial
+
* [http://www.w3schools.com/xml/default.asp W3Schools] Руководство по Xml
 +
 
 +
* [http://freepascal.ru/article//freepascal/20080321115724/ Thomas Zastrow article] Работа с XML-файлами
 +
 
 +
* [http://wiki.freepascal.org/TXMLPropStorage/ru TXMLPropStorage] использование TXMLPropStorage для сохранения настроек программы.
  
* [http://www.thomas-zastrow.de/texte/fpcxml/index.php Thomas Zastrow article] FPC and XML
+
== Смотрите также ==
  
[[Category:Free Component Library]]
+
* [[XML Decoders/ru|XML Документы]]
 +
* [[Using INI Files/ru|Использование INI файлов]]
 +
* [[fcl-xml]]
 +
* [[Internet Tools|Интернет-Инструменты]], для обработки XPath 2 / XQuery
 +
* "Web data formats in Lazarus/FPC" - статья  Michaël Van Canneyt ([https://www.freepascal.org/~michael/articles/webdata/webdata.pdf оригинал], [https://sites.google.com/view/leybasoft-arm/%D0%BF%D0%B5%D1%80%D0%B5%D0%B2%D0%BE%D0%B4%D1%8B/%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D1%8B-%D0%B2%D0%B5%D0%B1-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85-%D0%B2-lazarusfpc русский перевод])
 +
[[Category:Russian (unfinished translation)]]

Latest revision as of 09:13, 4 December 2020

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;
Light bulb  Примечание: Имена аттрибутов узла не могут начинаться с цифры! Если имена аттрибутов у узла одинаковые, то записывается значение последнего указанного аттрибута
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 (прим.перев. в примерах ниже для сохранениея XML-документа в файл используется перегруженная(overload) процедура WriteXMLFile)

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 для сохранения настроек программы.

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