Sign in Join
Wiki copytemporary

XML Tutorial/ru: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: XML Tutorial/ru

OldNewLine
1+XML - Расширяемый Язык Разметки (e**X**tensible **M**arkup **L**anguage) рекомендован [W3C](http://www.w3.org/) как язык для обмена информацией между различными системами. Это ориентированный на текст способ сохранения информации. Современные языки обмена данными, такие как XHTML, так же как и большинство технологий WebServices, основаны на XML.
2+
3+Это текстовый способ хранения информации, а не хранения информации в двоичном формате.
4+
5+Современные языки обмена данными, такие как XHTML, а также большинство технологий WebServices, основаны на XML. Эта вики может действительно дать только краткий обзор XML, при этом основное внимание уделяется анализу и использованию файлов XML в приложениях Free Pascal. Если вас интересует более полное объяснение XML и его использования, см. [здесь](https://ru.wikipedia.org/wiki/XML).
6+
7+## Введение
8+
9+В настоящее время в Free Pascal существует ряд модулей для поддержки XML. Эти модули называются "XMLRead", "XMLWrite" и "DOM", и являются частью Free Component Library (FCL) из комплекта Free Pascal. FCL уже находится в заданном по умолчанию пути поиска файлов для компилятора в Лазарусе, таким образом Вам нужно только добавить названия модулей в строку USES чтобы получить поддержку XML в Вашей программе. Использование XML пока не документировано, поэтому данная статья даёт необходимые вводные сведения для работы с модулями поддержки XML.
10+
11+DOM XML (Объектная модель документов) - это ряд стандартизированных объектов, которые предоставляют однотипный интерфейс для использования XML в различных языках и платформах. Стандарт определяет только методы, свойства и другие части интерфейса объекта, оставляя реализацию свободной для различных языков. FCL в настоящее время поддерживает полностью [DOM 1.0](http://ru.wikipedia.org/wiki/DOM).
12+
13+## Примеры
14+
15+В статье даны примеры работы с XML-данными по принципу нарастающей сложности.
16+
17+### Использование: Юникод или Ansi
18+
19+FPC предоставляет XML модули, использующие кодировку ANSI. Поэтому, они могут различаться в зависимости от платформы, в которой выполняется код и не иметь поддержку Юникода. Lazarus, в свою очередь, предоставляет собственный набор XML модулей, расположенных в пакете LazUtils. Они полностью поддерживают Юникод в формате UTF-8 и не зависят от выполняющей код платформы. Эти модули взаимозаменяемы и их использование определяется лишь наличием в списке uses.
20+
21+Следующие модули FPC XML используют системную кодировку:
22+
23+- DOM
24+- XMLRead
25+- XMLWrite
26+- XMLCfg
27+- XMLUtils
28+- XMLStreaming
29+
30+Эти модули предоставлены Lazarus XML и имеют поддержку Юникода в формате UTF-8:
31+
32+- laz2_DOM
33+- laz2_XMLRead
34+- laz2_XMLWrite
35+- laz2_XMLCfg
36+- laz2_XMLUtils
37+- laz_XMLStreaming.
38+
39+Не все из них нужны в каждом примере. Однако, вам понадобится DOM, т.к. в нем содержится несколько типов, включая [TXMLDocument](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/txmldocument.html).
40+
41+### Чтение текстового узла
42+
43+Для Delphi-программистов:
44+
45+Помните, что когда Вы работаете с TXMLDocument, текст в пределах узла считается **отдельным текстовым узлом**. Таким образом, Вы должны обратиться к текстовому значению узла, как к отдельному узлу. Альтернативно, свойство **TextContent** может быть использовано для возврата значения всех узлов лежащих ниже, которые связаны с данным.
46+
47+Процедура [**ReadXMLFile**](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_xmlread/readxmlfile.html) всегда создаёт новый **TXMLDocument**, таким образом Вы не должны создавать его заранее. Однако Вы должны вызывать метод **Free** вручную после окончания работы с документом для освобождения ресурсов занятых объектом **TXMLDocument**.
48+
49+Для примера рассмотрим следующий XML-файл:
50+
51+``` xml
52+ <?xml version="1.0"?>
53+ <request>
54+ <request_type>PUT_FILE</request_type>
55+ <username>123</username>
56+ <password>abc</password>
57+ </request>
58+```
59+
60+Следующий пример показывает корректный и некорректный способы получения значений текстового узла xml:
61+
62+``` pascal
63+ var
64+ PassNode: TDOMNode;
65+ Doc: TXMLDocument;
66+ begin
67+ // Читаем xml файл с жесткого диска
68+ ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');
69+ try
70+ // Запрашиваем узел с именем "password"
71+ PassNode := Doc.DocumentElement.FindNode('password');
72+ // Выводим значение выбранного узла
73+ //Неправильный способ
74+ WriteLn(PassNode.NodeValue); // вывод будет пустым
75+ //Правильный способ
76+ // Текст узла - это отдельный дочерний узел
77+ WriteLn(PassNode.FirstChild.NodeValue); // правильно выведет "abc"
78+ // Альтернативный способ
79+ WriteLn(PassNode.TextContent);
80+ finally
81+ // В завершении делаем Free для документа
82+ Doc.Free;
83+ end;
84+end;
85+```
86+
87+Обратите внимание, что ReadXMLFile (...) игнорирует все начальные символы пробелов при анализе документа. Раздел [[XML Tutorial/ru|символы пробелов]] описывает, как их сохранить.
88+
89+### Вывод имен узлов и атрибутов
90+
91+Маленькое замечание о навигации по дереву DOM:
92+
93+Для последовательного доступа к узлам лучше всего использовать свойства [FirstChild](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomnode.firstchild.html) и [NextSibling](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomnode.nextsibling.html) (чтобы шагать вперед по дереву) или [LastChild](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomnode.lastchild.html) и [PreviousSibling](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomnode.previoussibling.html) (назад с конца дерева). Для произвольного доступа к узлам дерева можно пользоваться функциями [FindNode](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomnode.findnode.html) (ищется первый узел верхнего уровня с подходящим именем) или [GetElementsByTagName](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomdocument.getelementsbytagname.html) (создается объект [TDOMNodeList](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomnodelist.html), который после использования должен быть освобождён). Это поведение отличается от других реализаций DOM (например, MSXML), поскольку FCL реализация основана на объектах, а не на интерфейсах.
94+
95+------------------------------------------------------------------------
96+
97+Прим.перев: кроме того, существует функция [GetNamedItem](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomnamednodemap.getnameditem.html) для поиска аттрибута по имени в текущем узле. Упрощенный пример (оригинал кода [здесь](https://forum.lazarus.freepascal.org/index.php/topic,50104.msg365179.html#msg365179)):
98+
99+``` pascal
100+var
101+ aNode, AttrNode: TDOMNode;
102+begin
103+...
104+ //в текущем узле aNode пытаемся найти аттрибут цвета с именем 'color' и присвоить его узлу AttrNode
105+ AttrNode:= aNode.Attributes.GetNamedItem('color');
106+ //если такой узел создан (и не равен nil)
107+ if Assigned(AttrNode) then
108+ begin
109+ //проверяем значение аттибута
110+ if AttrNode.NodeValue <> ''
111+ then
112+ //если значение задано, то считываем его и присваиваем свойству цвета Memo1
113+ Memo1.Color:= StringToColor(AttrNode.NodeValue)
114+ else
115+ Memo1.Color:= clInfoBk;
116+ end;
117+end;
118+```
119+
120+------------------------------------------------------------------------
121+
122+Следующий пример демонстрирует, как выводить имена узлов в компонент TMemo, расположенный на форме.
123+
124+Ниже приведён XML-файл с именем 'C:\Programs\test.xml':
125+
126+``` xml
127+ <?xml version="1.0"?>
128+ <images directory="mydir">
129+ <imageNode URL="graphic.jpg" title="">
130+ <Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca>
131+ <Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca>
132+ </imageNode>
133+ </images>
134+```
135+
136+И код на Pascal, который выполняет эту задачу:
137+
138+``` pascal
139+var
140+ Doc: TXMLDocument;
141+ Child: TDOMNode;
142+ j: Integer;
143+begin
144+ try
145+ ReadXMLFile(Doc, 'test.xml');
146+ Memo.Lines.Clear;
147+ // Используем свойства FirstChild и NextSibling
148+ Child := Doc.DocumentElement.FirstChild;
149+ while Assigned(Child) do
150+ begin
151+ Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);
152+ // Используем свойство ChildNodes
153+ with Child.ChildNodes do
154+ try
155+ for j := 0 to (Count - 1) do
156+ Memo.Lines.Add(format('%s %s (%s=%s; %s=%s)',
157+ [
158+ Item[j].NodeName,
159+ Item[j].FirstChild.NodeValue,
160+ Item[j].Attributes.Item[0].NodeName, // 1-й атрибут
161+ Item[j].Attributes.Item[0].NodeValue,
162+ Item[j].Attributes.Item[1].NodeName, // 2-й атрибут
163+ Item[j].Attributes.Item[1].NodeValue
164+ ]));
165+ finally
166+ Free;
167+ end;
168+ Child := Child.NextSibling;
169+ end;
170+ finally
171+ Doc.Free;
172+ end;
173+end;
174+```
175+
176+В результате программа выведет следующее:
177+
178+ imageNode graphic.jpg
179+ Peca Pecacastelo.jpg1.swf (DestinoX=0; DestinoY=0)
180+ Peca Pecacastelo.jpg2.swf (DestinoX=0; DestinoY=86)
181+
182+------------------------------------------------------------------------
183+
184+Прим. перев.: Один из участников форума Werner Pamler (a.k.a. [wp](https://forum.lazarus.freepascal.org/index.php?action=profile;u=45589)) [предложил](https://forum.lazarus.freepascal.org/index.php/topic,39432.msg270619.html#msg270619) рекурсивную функцию поиска по всему дереву первого подходящего дочернего узла по имени, начиная с указанного родительского узла:
185+
186+``` pascal
187+function FindNode(AParent: TDOMNode; AName: String): TDOMNode;
188+ var
189+ node: TDOMNode;
190+ begin
191+ Result := AParent.FindNode(AName);
192+ if Result = nil then begin
193+ node := AParent.FirstChild;
194+ while node <> nil do begin
195+ Result := FindNode(node, AName);
196+ if Result <> nil then
197+ exit;
198+ node := node.NextSibling;
199+ end;
200+ end;
201+ end;
202+```
203+
204+Пример использования:
205+
206+``` pascal
207+var aDoc: TXMLDocument;
208+ aChildNode: TDOMNode;
209+begin
210+ //будем считать, что aDoc уже заполнен данными
211+ ...
212+ //ищем дочерний узел с именем 'MyChildNode',
213+ //начиная с самого первого узла (aDoc.DocumentElement) документа aDoc
214+ aChildNode:= FindNode(aDoc.DocumentElement,'MyChildNode');
215+ if not Assigned(aNode) then
216+ showmessage('узла с именем "MyChildNode" не существует')
217+ else
218+ showmessage('узел с именем "MyChildNode" найден');
219+end;
220+```
221+
222+### Загрузка XML в TreeView
223+
224+Одно из обычных использований файла XML - разбор и показ информации в древовидном формате. Вы можете отыскать компонент TTreeView на вкладке "Common Controls" Lazarus'а.
225+
226+Функция, приведённая ниже, возмёт документ XML, предварительно загруженный из файла или сгенерированный программно, и заполнит TreeView его содержимым. Заголовком каждого узла будет содержимое первого атрибута этого узла.
227+
228+``` pascal
229+procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);
230+var
231+ iNode: TDOMNode;
232+
233+ procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);
234+ var
235+ cNode: TDOMNode;
236+ s: string;
237+ begin
238+ if Node = nil then Exit; // выходим, если достигнут конец документа
239+
240+ // добавляем узел в дерево
241+ if Node.HasAttributes and (Node.Attributes.Length>0) then
242+ s:=Node.Attributes[0].NodeValue
243+ else
244+ s:='';
245+ TreeNode := tree.Items.AddChild(TreeNode, s);
246+
247+ // переходим к дочернему узлу
248+ cNode := Node.FirstChild;
249+
250+ // проходим по всем дочерним узлам
251+ while cNode <> nil do
252+ begin
253+ ProcessNode(cNode, TreeNode);
254+ cNode := cNode.NextSibling;
255+ end;
256+ end;
257+
258+begin
259+ iNode := XMLDoc.DocumentElement.FirstChild;
260+ while iNode <> nil do
261+ begin
262+ ProcessNode(iNode, nil); // Рекурсия
263+ iNode := iNode.NextSibling;
264+ end;
265+end;
266+```
267+
268+Другой пример, который отображает полную структуру XML, включая все значения атрибутов (примечание: длинная строка, ссылающаяся на TreeView, была разделена, поэтому она будет переносить слова для этой вики; при записи в коде вам не нужно разбивать строку, если вам не нравится форматирование ):
269+
270+``` pascal
271+procedure XML2Tree(XMLDoc:TXMLDocument; TreeView:TTreeView);
272+
273+ //Локальная функция, которая выводит все атрибуты узла в виде строки
274+ function GetNodeAttributesAsString(pNode: TDOMNode):string;
275+ var i: integer;
276+ begin
277+ Result:='';
278+ if pNode.HasAttributes then
279+ for i := 0 to pNode.Attributes.Length -1 do
280+ with pNode.Attributes[i] do
281+ Result := Result + format(' %s="%s"', [NodeName, NodeValue]);
282+
283+ //Удаляем начальные и конечные пробелы
284+ Result:=Trim(Result);
285+ end;
286+
287+ // Рекурсивная функция для обработки узла и всех его дочерних узлов
288+
289+ procedure ParseXML(Node:TDOMNode; TreeNode: TTreeNode);
290+ begin
291+ //Выход из процедуры, если больше нет узлов для обработки
292+ if Node = nil then Exit;
293+
294+ //Добавляем узел в TreeView
295+ TreeNode := TreeView.Items.AddChild(TreeNode,
296+ Trim(Node.NodeName+' '+
297+ GetNodeAttributesAsString(Node)+
298+ Node.NodeValue)
299+ );
300+
301+ //Обрабатываем все дочерние узлы
302+ Node := Node.FirstChild;
303+ while Node <> Nil do
304+ begin
305+ ParseXML(Node, TreeNode);
306+ Node := Node.NextSibling;
307+ end;
308+ end;
309+
310+begin
311+ TreeView.Items.Clear;
312+ ParseXML(XMLDoc.DocumentElement,nil);
313+end;
314+```
315+
316+### Изменение XML документа
317+
318+Первая вещь, о которой следует помнить, TDOMDocument это хэндл (handle) к DOM. Вы можете получить экземпляр этого класса создавая или загружая XML документ.
319+
320+С другой стороны, узлы не могут быть созданы как обычные объекты. Вы должны использовать методы, которые предоставляет TDOMDocument для их создания и, впоследствии, использовать другие методы для помещения их в нужное место дерева. Это говорит о том, что узлы должны принадлежать вполне определённому документу DOM.
321+
322+Вот некоторые общераспространённые методы TDOMDocument:
323+
324+``` pascal
325+ function CreateElement(const tagName: DOMString): TDOMElement; virtual;
326+ function CreateTextNode(const data: DOMString): TDOMText;
327+ function CreateCDATASection(const data: DOMString): TDOMCDATASection; virtual;
328+ function CreateAttribute(const name: DOMString): TDOMAttr; virtual;
329+```
330+
331+[`CreateElement`](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomdocument.createelement.html) создает новый элемент.
332+
333+[`CreateTextNode`](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomdocument.createtextnode.html) создает текстовый узел.
334+
335+[`CreateAttribute`](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomdocument.createattribute.html) создает атрибут узла.
336+
337+[`CreateCDATASection`](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_dom/tdomdocument.createcdatasection.html) создает раздел CDATA: обычные символы разметки XML, такие как \<\>, не интерпретируются в разделе CDATA. См. [статью по CDATA на Wikipedia](https://ru.wikipedia.org/wiki/)
338+
339+Более удобный метод для управления атрибутами - использовать метод `TDOMElement.SetAttribute`, который также представлен как свойство по умолчанию `TDOMElement`:
340+
341+``` pascal
342+// эти две конструкции эквивалентны
343+Element.SetAttribute('name', 'value');
344+Element['name'] := 'value';
345+```
346+
347+------------------------------------------------------------------------
348+
349+Прим.перев.: вообще-то, автор оригинального текста немного ошибается, потому что выше приведенный код у вас просто не скомпилируется. Правильнее он выглядит так:
350+
351+``` pascal
352+var Element: TDOMNode;
353+begin
354+...
355+// эти две конструкции эквивалентны
356+TDOMElement(Element).SetAttribute('name', 'value');
357+TDOMElement(Element).AttribStrings['name']:= 'value';
358+...
359+end;
360+```
361+
362+> **Note:** Имена аттрибутов узла не могут начинаться с цифры! Если имена аттрибутов у узла одинаковые, то записывается значение последнего указанного аттрибута
363+
364+``` pascal
365+var Element: TDOMNode;
366+begin
367+...
368+TDOMElement(Element).SetAttribute('name', 'value 1');
369+TDOMElement(Element).SetAttribute('name', 'value 2');
370+//в файле мы увидим лишь <Element name="value 2">
371+...
372+end;
373+```
374+
375+------------------------------------------------------------------------
376+
377+Вот, в качестве примера, метод, который ищет выбранный элемент в TTreeView и затем вставляет дочерний узел в документ XML, где он должен находиться . TreeView должен быть предварительно заполнен содержанием из XML файла, используя [[XML Tutorial/ru|XML2Tree function]].
378+
379+``` pascal
380+procedure TForm1.actAddChildNode(Sender: TObject);
381+var
382+ position: Integer;
383+ NewNode: TDomNode;
384+begin
385+ {*******************************************************************
386+ * Определение выбраного элемента
387+ *******************************************************************}
388+ if TreeView1.Selected = nil then Exit;
389+
390+ if TreeView1.Selected.Level = 0 then
391+ begin
392+ position := TreeView1.Selected.Index;
393+
394+ NewNode := XMLDoc.CreateElement('item');
395+ TDOMElement(NewNode).SetAttribute('nome', 'Item');
396+ TDOMElement(NewNode).SetAttribute('file', 'File');
397+ with XMLDoc.DocumentElement.ChildNodes do
398+ begin
399+ Item[position].AppendChild(NewNode);
400+ Free;
401+ end;
402+
403+ {*******************************************************************
404+ * Обновление TreeView
405+ *******************************************************************}
406+ TreeView1.Items.Clear;
407+ XML2Tree(TreeView1, XMLDoc);
408+ end
409+ else if TreeView1.Selected.Level >= 1 then
410+ begin
411+ {*******************************************************************
412+ * Эта функция работает только на верхнем уровне дерева
413+ * и вы должны её модифицировать, чтобы использовать другие уровни
414+ *******************************************************************}
415+ end;
416+end;
417+```
418+
419+### Создание TXMLDocument из строки
420+
421+Если данные XML находятся в строке MyXmlString, создать из нее DOM можно с помощью следующего кода:
422+
423+``` pascal
424+var
425+ S: TStringStream;
426+ XML: TXMLDocument;
427+begin
428+ S := TStringStream.Create('');
429+ try
430+ //Читаем XML документ полностью
431+ ReadXMLFile(XML, S);
432+ //Альтернативно: чтение только фрагмента XML
433+ ReadXMLFragment(AParentNode, S);
434+ finally
435+ S.Free;
436+ end;
437+end;
438+```
439+
440+### Проверка достоверности документа
441+
442+Начиная с марта 2007, в FCL XML парсер добавлена возможность проверки достоверности (валидации) документа с помощью DTD. Проверка достоверности определяет соответствие логической структуры документа определенным правилам, заданных с помощью "Определение Типа документа" (DTD).
443+
444+Это пример XML-документа, содержащего DTD:
445+
446+``` xml
447+ <?xml version='1.0'?>
448+ <!DOCTYPE root [
449+ <!ELEMENT root (child)+ >
450+ <!ELEMENT child (#PCDATA)>
451+ ]>
452+ <root>
453+ <child>This is a first child.</child>
454+ <child>And this is the second one.</child>
455+ </root>
456+```
457+
458+Этот DTD определяет, что элемент `root` должен содержать один или более элемент `child`, а элементы `child` могут содержать только символьные данные. Если парсер обнаружит нарушение этих правил, то он сообщит о них.
459+
460+Загрузка такого документа несколько сложнее. Пусть исходные XML данные содержатся в потоке AStream:
461+
462+``` pascal
463+procedure TMyObject.DOMFromStream(AStream: TStream);
464+var
465+ Parser: TDOMParser;
466+ Src: TXMLInputSource;
467+ TheDoc: TXMLDocument;
468+begin
469+ try
470+ // Создаём объект-парсер
471+ Parser := TDOMParser.Create;
472+ // и источник данных
473+ Src := TXMLInputSource.Create(AStream);
474+ // Включаем проверку достоверности
475+ Parser.Options.Validate := True;
476+ // Назначаем обработчик ошибок, который будет получать уведомления о них
477+ Parser.OnError := @ErrorHandler;
478+ // А теперь работаем
479+ Parser.Parse(Src, TheDoc);
480+ finally
481+ // ... и убираем за собой :)
482+ Src.Free;
483+ Parser.Free;
484+ end;
485+end;
486+
487+procedure TMyObject.ErrorHandler(E: EXMLReadError);
488+begin
489+ if E.Severity = esError then // Нас интересуют только ошибки проверки достоверности
490+ writeln(E.Message);
491+end;
492+```
493+
494+### Cимволы пробелов
495+
496+Если необходимо сохранять пробельные символы в тексте узлов, следует пользоваться вышеприведённым методом загрузки XML документа. По умолчанию начальные пробельные символы игнорируются, именно поэтому функция ReadXML(...) пропускает все пробельные символы в начале текста узлов. Перед вызовом *Parser.Parse(Src, TheDoc)* добавьте следующую строку:
497+
498+``` pascal
499+Parser.Options.PreserveWhitespace := True;
500+```
501+
502+После этого парсер будет оставлять все пробельные символы, включая все переводы строк, добавленные для повышения читаемости XML документа!
503+
504+Используйте следующий код, чтобы у вас была процедура ReadXMLFile, которая сохраняет первые пробелы:
505+
506+``` pascal
507+procedure ReadXMLFilePreserveWhitespace(out Doc: TXMLDocument; FileName: string);
508+var
509+ Parser: TDOMParser;
510+ Src: TXMLInputSource;
511+ InFile: TFileStream;
512+begin
513+ try
514+ InFile := TFileStream.Create(FileName, fmOpenRead);
515+ Src := TXMLInputSource.Create(InFile);
516+ Parser := TDOMParser.Create;
517+ Parser.Options.PreserveWhitespace := True;
518+ Parser.Parse(Src, Doc);
519+ finally
520+ Src.Free;
521+ Parser.Free;
522+ InFile.Free;
523+ end;
524+end;
525+```
526+
527+### Потоковое чтение
528+
529+Обработка XML с использованием DOM, требует загрузки всего документа в память. Это может быть не желательно или невозможно, если документ огромен. FCL предоставляет функции для чтения одного узла данных XML за один раз, используя класс TXMLReader и его потомков. Класс TXMLReader похож на класс XmlReader платформы .NET. Ниже приведен простой пример использования TXmlReader:
530+
531+``` pascal
532+uses
533+ Classes,xmlreader,xmltextreader,xmlutils;
534+
535+procedure readme(AStream: TStream);
536+var
537+ xtr: TXmlReader;
538+ settings: TXMLReaderSettings;
539+ inp: TXMLInputSource;
540+begin
541+ settings := TXMLReaderSettings.Create;
542+ try
543+ settings.PreserveWhiteSpace := True;
544+ settings.Namespaces := True;
545+ inp := TXMLInputSource.Create(AStream);
546+ try
547+ xtr := TXmlTextReader.Create(inp,settings);
548+ try
549+ // Здесь начинается чтение
550+ while xtr.Read do
551+ begin
552+ write(xtr.NodeType:25);
553+ if xtr.name<>'' then
554+ write(xtr.Name:9)
555+ else
556+ write('*no name* ');
557+ write(xtr.Value);
558+ writeln;
559+ if xtr.NodeType=ntElement then
560+ begin
561+ // вывод атрибутов
562+ if xtr.MoveToFirstAttribute then
563+ begin
564+ repeat
565+ writeln('---',xtr.NodeType:21,xtr.Name:10,xtr.Value:10);
566+ until not xtr.MoveToNextAttribute;
567+ xtr.MoveToContent;
568+ end;
569+ end;
570+ end;
571+ // Очистка
572+ finally
573+ xtr.Free;
574+ end;
575+ finally
576+ inp.Free;
577+ end;
578+ finally
579+ settings.Free;
580+ end;
581+end;
582+```
583+
584+### Генерация файла XML
585+
586+Ниже приведён код для записи XML в файле. (Взято из обучающей программы в блоге DeveLazarus). Пожалуйста помните, что модули DOM и XMLWrite должны быть включены в Uses (прим.перев. в примерах ниже для сохранениея XML-документа в файл используется перегруженная(overload) процедура [WriteXMLFile](https://lazarus-ccr.sourceforge.io/docs/lazutils/laz2_xmlwrite/writexmlfile.html))
587+
588+``` pascal
589+unit Unit1;
590+
591+{$mode objfpc}{$H+}
592+
593+interface
594+
595+uses
596+ Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,
597+ DOM, XMLWrite;
598+
599+type
600+ { TForm1 }
601+ TForm1 = class(TForm)
602+ Button1: TButton;
603+ Label1: TLabel;
604+ Label2: TLabel;
605+ procedure Button1Click(Sender: TObject);
606+ private
607+ { private declarations }
608+ public
609+ { public declarations }
610+ end;
611+
612+var
613+ Form1: TForm1;
614+
615+implementation
616+
617+{ TForm1 }
618+
619+procedure TForm1.Button1Click(Sender: TObject);
620+var
621+ XDoc: TXMLDocument; // переменная документа
622+ RootNode, ParentNode, ChildNode: TDOMNode; // переменные узлов документа
623+begin
624+ //Создаём документ
625+ XDoc := TXMLDocument.create;
626+
627+ //Создаём корневой узел
628+ RootNode := XDoc.CreateElement('register');
629+ XDoc.Appendchild(RootNode); // Добавляем корневой узел в документ
630+
631+ RootNode:= XDoc.DocumentElement; //назначаем Root-узлом текущий узел
632+ ParentNode:= XDoc.CreateElement('user'); //создаём родительский узел
633+ TDOMElement(ParentNode).SetAttribute('id', '001'); // создаём атрибуты родительского узла
634+ RootNode.Appendchild(ParentNode); // добавляем родительский узел <user id="001"></user> внутри корневого <register></register>
635+
636+ ParentNode:= XDoc.CreateElement('name'); //создаём дочерний по отношению к <user id="001"></user> узел
637+ //TDOMElement(ParentNode).SetAttribute('sex', 'M');
638+ ChildNode:= XDoc.CreateTextNode('Fernando'); // вставляем значение в узел
639+ ParentNode.Appendchild(ChildNode); // сохраняем дочерний узел внутри родителя
640+ RootNode.ChildNodes.Item[0].AppendChild(ParentNode); // вставляем дочерний узел <name>Fernando</name> в соответствующий родительский узел
641+ // <user id="001"></user> (он будет в корневом узле RootNode первым - отсчет с нуля - RootNode.ChildNodes.Item[0])
642+
643+ //Создаём ещё один дочерний узел (аналогично выше описанному)
644+ ParentNode := XDoc.CreateElement('age');
645+ //TDOMElement(parentNode).SetAttribute('year', '1976');
646+ ChildNode:= XDoc.CreateTextNode('32'); // вставляем значение в узел
647+ ParentNode.Appendchild(ChildNode); // сохраняем узел
648+ RootNode.ChildNodes.Item[0].AppendChild(ParentNode); // вставляем дочерний узел в соответствующий родительский
649+
650+ //создаем строку вида внутри тега <user id="001"></user>
651+ ChildNode:= ADoc.CreateComment('это коммент');
652+ RootNode.ChildNodes.Item[0].AppendChild(ChildNode);
653+
654+ //создаем строку вида <![CDATA[это CDATASection]]> внутри тега <user id="001"></user>
655+ ChildNode:= ADoc.CreateCDATASection('это CDATASection');
656+ RootNode.ChildNodes.Item[0].AppendChild(ChildNode);
657+
658+ writeXMLFile(XDoc,'test.xml'); // записываем всё в XML-файл
659+ XDoc.free; // освобождаем память
660+end;
661+
662+initialization
663+ {$I unit1.lrs}
664+
665+end.
666+```
667+
668+Вот такой XML-файл у нас должен получится в результате:
669+
670+``` xml
671+<?xml version="1.0"?>
672+<register>
673+ <user id="001">
674+ <name>Fernando</name>
675+ <age>32</age>
676+
677+ <![CDATA[это CDATASection]]>
678+ </user>
679+</register>
680+```
681+
682+------------------------------------------------------------------------
683+
684+Прим.переводчика: обратите внимание, что функция `function CreateElement(const tagName: DOMString): TDOMElement; virtual;` допускает в качестве агрумента строки, содержащие **только цифры и буквы латиницы без пробелов**, т.е. в выше приведенном примере попытка написать:
685+
686+``` pascal
687+...
688+RootNode:= xdoc.CreateElement('my register');
689+//или RootNode:= xdoc.CreateElement('регданные');
690+//или RootNode:= xdoc.CreateElement('re.gister');
691+...
692+```
693+
694+немедленно вызовет исключение "EDOMError in DOMDocument.CreateElement" с кодом ошибки INVALID_CHARACTER_ERR.
695+
696+То же самое правило справедливо и для имен аттрибута/ов узла - см. мое замечание [[XML Tutorial/ru|выше]].
697+
698+------------------------------------------------------------------------
699+
700+Пример, где вам не нужно ссылаться на элемент по индексу.
701+
702+``` pascal
703+procedure TForm1.Button2Click(Sender: TObject);
704+var
705+ Doc: TXMLDocument;
706+ RootNode, ElementNode,ItemNode,TextNode: TDOMNode;
707+ i: integer;
708+begin
709+ try
710+ // создаем документ
711+ Doc := TXMLDocument.Create;
712+ // создаем корневой узел
713+ RootNode := Doc.CreateElement('Root');
714+ Doc.Appendchild(RootNode);
715+ RootNode:= Doc.DocumentElement;
716+ // создаем узлы
717+ for i := 1 to 20 do
718+ begin
719+ ElementNode:=Doc.CreateElement('Element');
720+ TDOMElement(ElementNode).SetAttribute('id', IntToStr(i));
721+
722+ ItemNode:=Doc.CreateElement('Item1');
723+ TDOMElement(ItemNode).SetAttribute('Attr1', IntToStr(i));
724+ TDOMElement(ItemNode).SetAttribute('Attr2', IntToStr(i));
725+ TextNode:=Doc.CreateTextNode('Item1Value is '+IntToStr(i));
726+ ItemNode.AppendChild(TextNode);
727+ ElementNode.AppendChild(ItemNode);
728+
729+ ItemNode:=Doc.CreateElement('Item2');
730+ TDOMElement(ItemNode).SetAttribute('Attr1', IntToStr(i));
731+ TDOMElement(ItemNode).SetAttribute('Attr2', IntToStr(i));
732+ TextNode:=Doc.CreateTextNode('Item2Value is '+IntToStr(i));
733+ ItemNode.AppendChild(TextNode);
734+ ElementNode.AppendChild(ItemNode);
735+
736+ RootNode.AppendChild(ElementNode);
737+ end;
738+ // сохраняем XML
739+ WriteXMLFile(Doc,'TestXML_v2.xml');
740+ finally
741+ Doc.Free;
742+ end;
743+```
744+
745+Сгенерированный XML:
746+
747+``` xml
748+<?xml version="1.0"?>
749+<Root>
750+ <Element id="1">
751+ <Item1 Attr1="1" Attr2="1">Item1Value is 1</Item1>
752+ <Item2 Attr1="1" Attr2="1">Item2Value is 1</Item2>
753+ </Element>
754+ <Element id="2">
755+ <Item1 Attr1="2" Attr2="2">Item1Value is 2</Item1>
756+ <Item2 Attr1="2" Attr2="2">Item2Value is 2</Item2>
757+ </Element>
758+ <Element id="3">
759+ <Item1 Attr1="3" Attr2="3">Item1Value is 3</Item1>
760+ <Item2 Attr1="3" Attr2="3">Item2Value is 3</Item2>
761+ </Element>
762+</Root>
763+```
764+
765+### Кодировки
766+
767+Начинаясь с ревизии SVN 12582, XMLReader может читать данные в любой кодировке при условии использования внешнего декодера. Смотрите [[XML Decoders/ru]] для более детальной информации.
768+
769+Согласно спецификации XML, атрибут encoding в первой строке XML является необязательным в случае, если фактическая кодировка - UTF-8 или UTF-16 (что определяется наличием BOM). В версии Lazarus 0.9.26 TXMLDocument имеет свойство Encoding, но оно игнорируется. При этом процедура WriteXMLFile всегда использует кодировку UTF-8 и не генерирует атрибут encoding в первой строке файла XML.
770+
771+## Дополнительные ссылки
772+
773+- [W3Schools](http://www.w3schools.com/xml/default.asp) Руководство по Xml
774+
775+- [Thomas Zastrow article](http://freepascal.ru/article//freepascal/20080321115724/) Работа с XML-файлами
776+
777+- [TXMLPropStorage](http://wiki.freepascal.org/TXMLPropStorage/ru) использование TXMLPropStorage для сохранения настроек программы.
778+
779+## Смотрите также
780+
781+- [[XML Decoders/ru|XML Документы]]
782+- [[Using INI Files/ru|Использование INI файлов]]
783+- [[fcl-xml]]
784+- [[Internet Tools|Интернет-Инструменты]], для обработки XPath 2 / XQuery
785+- "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))
786+
787+---
788+*From the Free Pascal wiki, page "XML Tutorial/ru", as it stood on 2020-12-04 after a change by Zoltanleo, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
789+Category: Russian (unfinished translation)

Back to history