Sign in Join
Wiki copytemporary

XML Tutorial: what changed

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

Title: XML Tutorial

OldNewLine
1+The Extensible Markup Language (XML) is a World Wide Web Consortium (or "[W3C](http://www.w3.org/)") recommended language created to interchange information between different systems.
2+
3+It is a text based way to store information as opposed to storing the information in a binary format.
4+
5+Modern data interchange languages such as XHTML, as well as most WebServices technologies, are based on XML. This wiki can only really give a thumbnail overview of XML, with the primary focus being the parsing and use of XML files in Free Pascal applications. If you are interested in a more comprehensive explanation of XML and how it is used, see <http://en.wikipedia.org/wiki/XML>.
6+
7+## Introduction
8+
9+Currently there is a set of units that provides support for XML on Free Pascal. These units are called "XMLRead", "XMLWrite" and "DOM" and they are part of the Free Component Library (FCL) from the Free Pascal Compiler. The FCL is already on the default search path for the compiler on Lazarus, so you only need to add the units to your uses clause in order to get XML support. The FCL is not documented currently (October / 2005), so this short tutorial aims at introducing XML access using those units.
10+
11+The XML DOM (Document Object Model) is a set of standardized objects that provide a similar interface for using XML on different languages and systems. The standard only specifies the methods, properties and other interface parts of the object, leaving the implementation free for different languages. The FCL currently fully supports the [XML DOM 2.0](http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/) and a subset of [XML DOM 3.0](http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/) listed [[dom|here]].
12+
13+## Usage Examples
14+
15+Below there is a list of XML data manipulation examples with growing complexity.
16+
17+### Units in the uses clause
18+
19+FPC comes with XML units that supports UTF8 and UTF16. They may not have the latest updates, so there is as well a version of those units (prefixed with "laz2_") in the Lazarus package LazUtils. The units are compatible and one can change from to the other just by changing the uses clause.
20+
21+The units for using the FPC XML support which uses strings encoded in the system encoding are:
22+
23+- DOM
24+- XMLRead
25+- XMLWrite
26+- XMLCfg
27+- XMLUtils
28+- XMLStreaming
29+
30+The units for using the Lazarus XML support which has full UTF-8 Unicode support are:
31+
32+- laz2_DOM
33+- laz2_XMLRead
34+- laz2_XMLWrite
35+- laz2_XMLCfg
36+- laz2_XMLUtils
37+- laz_XMLStreaming.
38+
39+Not all of them are needed in every example, though. You will need DOM as it defines several types including TXMLDocument.
40+
41+### Reading a text node
42+
43+For Delphi Programmers:
44+
45+Note that when working with TXMLDocument, the text within a Node is considered a separate TEXT Node. As 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.
46+
47+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.
48+
49+For instance, consider the following 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+The following code example shows both the correct and the incorrect ways of getting the value of the text node (add the units **laz2_XMLRead** and **laz2_DOM** to the used units list):
61+
62+``` pascal
63+var
64+ PassNode: TDOMNode;
65+ Doc: TXMLDocument;
66+begin
67+ try
68+ // Read in xml file from disk
69+ ReadXMLFile(Doc, 'test.xml');
70+ // Retrieve the "password" node
71+ PassNode := Doc.DocumentElement.FindNode('password');
72+ // Write out value of the selected node
73+ WriteLn(PassNode.NodeValue); // will be blank
74+ // The text of the node is actually a separate child node
75+ WriteLn(PassNode.FirstChild.NodeValue); // correctly prints "abc"
76+ // alternatively
77+ WriteLn(PassNode.TextContent);
78+ finally
79+ // finally, free the document
80+ Doc.Free;
81+ end;
82+end;
83+```
84+
85+Note that ReadXMLFile(...) ignores all leading whitespace characters when parsing a document. The section whitespace characters describes how to keep them.
86+
87+### Printing the names of nodes and attributes
88+
89+If you want to navigate the DOM tree: when you need to access nodes in sequence, it is best to use **FirstChild** and **NextSibling** properties (to iterate forward), or **LastChild** and **PreviousSibling** (to iterate backward).
90+
91+For random access it is possible to use **ChildNodes** or **GetElementsByTagName** methods, but these will create a TDOMNodeList object which eventually must be freed. This differs from other DOM implementations like MSXML, because the FCL implementation is object-based, not interface-based.
92+
93+The following example shows how to print the names of nodes to a TMemo placed on a form.
94+
95+Below is the XML file called 'test.xml':
96+
97+``` xml
98+<?xml version="1.0"?>
99+<images directory="mydir">
100+ <imageNode URL="graphic.jpg" title="">
101+ <Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca>
102+ <Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca>
103+ </imageNode>
104+</images>
105+```
106+
107+And here the Pascal code to execute the task:
108+
109+``` pascal
110+var
111+ Doc: TXMLDocument;
112+ Child: TDOMNode;
113+ j: Integer;
114+begin
115+ try
116+ ReadXMLFile(Doc, 'test.xml');
117+ Memo.Lines.Clear;
118+ // using FirstChild and NextSibling properties
119+ Child := Doc.DocumentElement.FirstChild;
120+ while Assigned(Child) do
121+ begin
122+ Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);
123+ // using ChildNodes method
124+ with Child.ChildNodes do
125+ try
126+ for j := 0 to (Count - 1) do
127+ Memo.Lines.Add(format('%s %s (%s=%s; %s=%s)',
128+ [
129+ Item[j].NodeName,
130+ Item[j].FirstChild.NodeValue,
131+ Item[j].Attributes.Item[0].NodeName, // 1st attribute details
132+ Item[j].Attributes.Item[0].NodeValue,
133+ Item[j].Attributes.Item[1].NodeName, // 2nd attribute details
134+ Item[j].Attributes.Item[1].NodeValue
135+ ]));
136+ finally
137+ Free;
138+ end;
139+ Child := Child.NextSibling;
140+ end;
141+ finally
142+ Doc.Free;
143+ end;
144+end;
145+```
146+
147+This will print:
148+
149+ imageNode graphic.jpg
150+ Peca Pecacastelo.jpg1.swf (DestinoX=0; DestinoY=0)
151+ Peca Pecacastelo.jpg2.swf (DestinoX=0; DestinoY=86)
152+
153+### Populating a TreeView with XML
154+
155+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.
156+
157+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.
158+
159+``` pascal
160+procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);
161+var
162+ iNode: TDOMNode;
163+
164+ procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);
165+ var
166+ cNode: TDOMNode;
167+ s: string;
168+ begin
169+ if Node = nil then Exit; // Stops if reached a leaf
170+
171+ // Adds a node to the tree
172+ if Node.HasAttributes and (Node.Attributes.Length>0) then
173+ s := Node.Attributes[0].NodeValue
174+ else
175+ s := '';
176+ TreeNode := tree.Items.AddChild(TreeNode, s);
177+
178+ // Goes to the child node
179+ cNode := Node.FirstChild;
180+
181+ // Processes all child nodes
182+ while cNode <> nil do
183+ begin
184+ ProcessNode(cNode, TreeNode);
185+ cNode := cNode.NextSibling;
186+ end;
187+ end;
188+
189+begin
190+ iNode := XMLDoc.DocumentElement.FirstChild;
191+ while iNode <> nil do
192+ begin
193+ ProcessNode(iNode, nil); // Recursive
194+ iNode := iNode.NextSibling;
195+ end;
196+end;
197+```
198+
199+Another example that displays the complete XML structure including all attribute values (note: the long line referencing TreeView has been split so it will word wrap for this wiki; when writing it in code you do not have to break the line unless you like the formatting) :
200+
201+``` pascal
202+procedure XML2Tree(XMLDoc:TXMLDocument; TreeView:TTreeView);
203+
204+ // Local function that outputs all node attributes as a string
205+ function GetNodeAttributesAsString(pNode: TDOMNode):string;
206+ var i: integer;
207+ begin
208+ Result:='';
209+ if pNode.HasAttributes then
210+ for i := 0 to pNode.Attributes.Length -1 do
211+ with pNode.Attributes[i] do
212+ Result := Result + format(' %s="%s"', [NodeName, NodeValue]);
213+
214+ // Remove leading and trailing spaces
215+ Result:=Trim(Result);
216+ end;
217+
218+ // Recursive function to process a node and all its child nodes
219+
220+ procedure ParseXML(Node:TDOMNode; TreeNode: TTreeNode);
221+ begin
222+ // Exit procedure if no more nodes to process
223+ if Node = nil then Exit;
224+
225+ // Add node to TreeView
226+ TreeNode := TreeView.Items.AddChild(TreeNode,
227+ Trim(Node.NodeName+' '+
228+ GetNodeAttributesAsString(Node)+
229+ Node.NodeValue)
230+ );
231+
232+ // Process all child nodes
233+ Node := Node.FirstChild;
234+ while Node <> Nil do
235+ begin
236+ ParseXML(Node, TreeNode);
237+ Node := Node.NextSibling;
238+ end;
239+ end;
240+
241+begin
242+ TreeView.Items.Clear;
243+ ParseXML(XMLDoc.DocumentElement,nil);
244+end;
245+```
246+
247+### Modifying a XML document
248+
249+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.
250+
251+Nodes on the other hand cannot be created like a normal object. You *must* use the methods provided by TDOMDocument to create them, and later use other methods to put them in the correct place in the tree. This is because nodes must be "owned" by a specific document in DOM.
252+
253+Below are some common methods from TDOMDocument:
254+
255+``` pascal
256+function CreateElement(const tagName: DOMString): TDOMElement; virtual;
257+function CreateTextNode(const data: DOMString): TDOMText;
258+function CreateCDATASection(const data: DOMString): TDOMCDATASection; virtual;
259+function CreateAttribute(const name: DOMString): TDOMAttr; virtual;
260+```
261+
262+`CreateElement` creates a new element.
263+
264+`CreateTextNode` creates a text node.
265+
266+`CreateAttribute` creates an attribute node.
267+
268+`CreateCDATASection` creates a CDATA section: regular XML markup characters such as \<\> are not interpreted within the CDATA section. See [Wikipedia article on CDATA](https://secure.wikimedia.org/wikipedia/en/wiki/CDATA)
269+
270+A more convenient method to manipulate attributes is to use `TDOMElement.SetAttribute` method, which is also represented as the default property of `TDOMElement`:
271+
272+``` pascal
273+// these two statements are equivalent
274+Element.SetAttribute('name', 'value');
275+Element['name'] := 'value';
276+```
277+
278+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 an XML file using the [[XML Tutorial|XML2Tree function]].
279+
280+``` pascal
281+procedure TForm1.actAddChildNode(Sender: TObject);
282+var
283+ position: Integer;
284+ NovoNo: TDomNode;
285+begin
286+ {*******************************************************************
287+ * Detects the selected element
288+ *******************************************************************}
289+ if TreeView1.Selected = nil then Exit;
290+
291+ if TreeView1.Selected.Level = 0 then
292+ begin
293+ position := TreeView1.Selected.Index;
294+
295+ NovoNo := XMLDoc.CreateElement('item');
296+ TDOMElement(NovoNo).SetAttribute('nome', 'Item');
297+ TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo');
298+ with XMLDoc.DocumentElement.ChildNodes do
299+ begin
300+ Item[position].AppendChild(NovoNo);
301+ Free;
302+ end;
303+
304+ {*******************************************************************
305+ * Updates the TreeView
306+ *******************************************************************}
307+ TreeView1.Items.Clear;
308+ XML2Tree(TreeView1, XMLDoc);
309+ end
310+ else if TreeView1.Selected.Level >= 1 then
311+ begin
312+ {*******************************************************************
313+ * This function only works on the first level of the tree,
314+ * but can easily be modified to work for any number of levels
315+ *******************************************************************}
316+ end;
317+end;
318+```
319+
320+### Create a TXMLDocument from a string
321+
322+Given an XML document in string variable *MyXmlString*, the following code will create it's DOM:
323+
324+``` pascal
325+var
326+ S: TStringStream;
327+ XML: TXMLDocument;
328+begin
329+ S := TStringStream.Create('');
330+ try
331+ // Read complete XML document
332+ ReadXMLFile(XML, S);
333+ // Alternatively: read only an XML Fragment
334+ ReadXMLFragment(AParentNode, S);
335+ finally
336+ S.Free;
337+ end;
338+end;
339+```
340+
341+### Validating a document
342+
343+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).
344+
345+Here is an example of XML document with a DTD:
346+
347+``` xml
348+<?xml version='1.0'?>
349+<!DOCTYPE root [
350+<!ELEMENT root (child)+ >
351+<!ELEMENT child (#PCDATA)>
352+]>
353+<root>
354+ <child>This is a first child.</child>
355+ <child>And this is the second one.</child>
356+</root>
357+```
358+
359+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.
360+
361+Loading such document is slightly more complicated. Let's assume we have XML data in a TStream object:
362+
363+``` pascal
364+procedure TMyObject.DOMFromStream(AStream: TStream);
365+var
366+ Parser: TDOMParser;
367+ Src: TXMLInputSource;
368+ TheDoc: TXMLDocument;
369+begin
370+ try
371+ // create a parser object
372+ Parser := TDOMParser.Create;
373+ // and the input source
374+ Src := TXMLInputSource.Create(AStream);
375+ // we want validation
376+ Parser.Options.Validate := True;
377+ // assign a error handler which will receive notifications
378+ Parser.OnError := @ErrorHandler;
379+ // now do the job
380+ Parser.Parse(Src, TheDoc);
381+ // ...and cleanup
382+ finally
383+ Src.Free;
384+ Parser.Free;
385+ end;
386+end;
387+
388+procedure TMyObject.ErrorHandler(E: EXMLReadError);
389+begin
390+ if E.Severity = esError then // we are interested in validation errors only
391+ writeln(E.Message);
392+end;
393+```
394+
395+### Whitespace characters
396+
397+If you want to preserve leading whitespace characters in node texts, the above method is the way to load your XML document. Leading whitespace characters are ignored by default. That is the reason why the ReadXML(...) function never returns any leading whitespace characters in node texts. Before calling *Parser.Parse(Src, TheDoc)* insert the line
398+
399+``` pascal
400+Parser.Options.PreserveWhitespace := True;
401+```
402+
403+This will force the parser to return all whitespace characters including all the newline characters that exist in an XML document to make it more readable!
404+
405+The following code is similar to the `ReadXMLFile()` procedure, but preserves leading whitespace:
406+
407+``` pascal
408+procedure ReadXMLFilePreserveWhitespace(out Doc: TXMLDocument; FileName: string);
409+var
410+ Parser: TDOMParser;
411+ Src: TXMLInputSource;
412+ InFile: TFileStream;
413+begin
414+ try
415+ InFile := TFileStream.Create(FileName, fmOpenRead);
416+ Src := TXMLInputSource.Create(InFile);
417+ Parser := TDOMParser.Create;
418+ Parser.Options.PreserveWhitespace := True;
419+ Parser.Parse(Src, Doc);
420+ finally
421+ Src.Free;
422+ Parser.Free;
423+ InFile.Free;
424+ end;
425+end;
426+```
427+
428+Note that the xml document `Doc` is an `out` parameter created by the local `Parser`. It is the user's responsibility to free the `Doc` himself when it is no longer needed.
429+
430+### Streamed reading
431+
432+DOM-based processing requires the entire document loaded into memory. This may be not desirable, or not possible if document is huge. FCL provides functionality to read XML data one node at a time, using TXMLReader class and its descendants. This is similar to SAX, but works without callbacks. TXMLReader closely resembles .NET XmlReader class. A basic example follows:
433+
434+``` pascal
435+uses
436+ Classes,xmlreader,xmltextreader,xmlutils;
437+
438+procedure readme(AStream: TStream);
439+var
440+ xtr: TXmlReader;
441+ settings: TXMLReaderSettings;
442+ inp: TXMLInputSource;
443+begin
444+ settings := TXMLReaderSettings.Create;
445+ try
446+ settings.PreserveWhiteSpace := True;
447+ settings.Namespaces := True;
448+ inp := TXMLInputSource.Create(AStream);
449+ try
450+ xtr := TXmlTextReader.Create(inp,settings);
451+ try
452+ // Here the reading starts
453+ while xtr.Read do
454+ begin
455+ write(xtr.NodeType:25);
456+ if xtr.name<>'' then
457+ write(xtr.Name:9)
458+ else
459+ write('*no name* ');
460+ write(xtr.Value);
461+ writeln;
462+ if xtr.NodeType=ntElement then
463+ begin
464+ // print attributes
465+ if xtr.MoveToFirstAttribute then
466+ begin
467+ repeat
468+ writeln('---',xtr.NodeType:21,xtr.Name:10,xtr.Value:10);
469+ until not xtr.MoveToNextAttribute;
470+ xtr.MoveToContent;
471+ end;
472+ end;
473+ end;
474+ // Cleanup follows
475+ finally
476+ xtr.Free;
477+ end;
478+ finally
479+ inp.Free;
480+ end;
481+ finally
482+ settings.Free;
483+ end;
484+end;
485+```
486+
487+### Generating a XML file
488+
489+Below is the complete code to write a XML file. (This was taken from a tutorial in the DeveLazarus blog) Please, remember to include the DOM and XMLWrite units in your uses clause.
490+
491+``` pascal
492+unit Unit1;
493+
494+{$mode objfpc}{$H+}
495+
496+interface
497+
498+uses
499+ Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,
500+ DOM, XMLWrite;
501+
502+type
503+ { TForm1 }
504+ TForm1 = class(TForm)
505+ Button1: TButton;
506+ Label1: TLabel;
507+ Label2: TLabel;
508+ procedure Button1Click(Sender: TObject);
509+ private
510+ { private declarations }
511+ public
512+ { public declarations }
513+ end;
514+
515+var
516+ Form1: TForm1;
517+
518+implementation
519+
520+{ TForm1 }
521+
522+procedure TForm1.Button1Click(Sender: TObject);
523+var
524+ Doc: TXMLDocument; // variable to document
525+ RootNode, parentNode, nofilho: TDOMNode; // variable to nodes
526+begin
527+ try
528+ // Create a document
529+ Doc := TXMLDocument.Create;
530+
531+ // Create a root node
532+ RootNode := Doc.CreateElement('register');
533+ Doc.Appendchild(RootNode); // save root node
534+
535+ // Create a parent node
536+ RootNode:= Doc.DocumentElement;
537+ parentNode := Doc.CreateElement('usuario');
538+ TDOMElement(parentNode).SetAttribute('id', '001'); // create atributes to parent node
539+ RootNode.Appendchild(parentNode); // save parent node
540+
541+ // Create a child node
542+ parentNode := Doc.CreateElement('nome'); // create a child node
543+ // TDOMElement(parentNode).SetAttribute('sexo', 'M'); // create atributes
544+ nofilho := Doc.CreateTextNode('Fernando'); // insert a value to node
545+ parentNode.Appendchild(nofilho); // save node
546+ RootNode.ChildNodes.Item[0].AppendChild(parentNode); // insert child node in respective parent node
547+
548+ // Create a child node
549+ parentNode := Doc.CreateElement('idade'); // create a child node
550+ // TDOMElement(parentNode).SetAttribute('ano', '1976'); // create atributes
551+ nofilho := Doc.CreateTextNode('32'); // insert a value to node
552+ parentNode.Appendchild(nofilho); // save node
553+ RootNode.ChildNodes.Item[0].AppendChild(parentNode); // insert a childnode in respective parent node
554+
555+ writeXMLFile(Doc, 'test.xml'); // write to XML
556+ finally
557+ Doc.Free; // free memory
558+ end;
559+end;
560+
561+initialization
562+ {$I unit1.lrs}
563+
564+end.
565+```
566+
567+The result will be the XML file below:
568+
569+``` xml
570+<?xml version="1.0"?>
571+<register>
572+ <usuario id="001">
573+ <nome>Fernando</nome>
574+ <idade>32</idade>
575+ </usuario>
576+</register>
577+```
578+
579+An example where you don't need to reference an item by index.
580+
581+``` pascal
582+procedure TForm1.Button2Click(Sender: TObject);
583+var
584+ Doc: TXMLDocument;
585+ RootNode, ElementNode,ItemNode,TextNode: TDOMNode;
586+ i: integer;
587+begin
588+ try
589+ // Create a document
590+ Doc := TXMLDocument.Create;
591+ // Create a root node
592+ RootNode := Doc.CreateElement('Root');
593+ Doc.Appendchild(RootNode);
594+ RootNode:= Doc.DocumentElement;
595+ // Create nodes
596+ for i := 1 to 20 do
597+ begin
598+ ElementNode:=Doc.CreateElement('Element');
599+ TDOMElement(ElementNode).SetAttribute('id', IntToStr(i));
600+
601+ ItemNode:=Doc.CreateElement('Item1');
602+ TDOMElement(ItemNode).SetAttribute('Attr1', IntToStr(i));
603+ TDOMElement(ItemNode).SetAttribute('Attr2', IntToStr(i));
604+ TextNode:=Doc.CreateTextNode('Item1Value is '+IntToStr(i));
605+ ItemNode.AppendChild(TextNode);
606+ ElementNode.AppendChild(ItemNode);
607+
608+ ItemNode:=Doc.CreateElement('Item2');
609+ TDOMElement(ItemNode).SetAttribute('Attr1', IntToStr(i));
610+ TDOMElement(ItemNode).SetAttribute('Attr2', IntToStr(i));
611+ TextNode:=Doc.CreateTextNode('Item2Value is '+IntToStr(i));
612+ ItemNode.AppendChild(TextNode);
613+ ElementNode.AppendChild(ItemNode);
614+
615+ RootNode.AppendChild(ElementNode);
616+ end;
617+ // Save XML
618+ WriteXMLFile(Doc,'TestXML_v2.xml');
619+ finally
620+ Doc.Free;
621+ end;
622+```
623+
624+Generated XML:
625+
626+``` xml
627+<?xml version="1.0"?>
628+<Root>
629+ <Element id="1">
630+ <Item1 Attr1="1" Attr2="1">Item1Value is 1</Item1>
631+ <Item2 Attr1="1" Attr2="1">Item2Value is 1</Item2>
632+ </Element>
633+ <Element id="2">
634+ <Item1 Attr1="2" Attr2="2">Item1Value is 2</Item1>
635+ <Item2 Attr1="2" Attr2="2">Item2Value is 2</Item2>
636+ </Element>
637+ <Element id="3">
638+ <Item1 Attr1="3" Attr2="3">Item1Value is 3</Item1>
639+ <Item2 Attr1="3" Attr2="3">Item2Value is 3</Item2>
640+ </Element>
641+</Root>
642+```
643+
644+### Encoding
645+
646+Starting from FPC version 2.4, the XML reader is able to process data in any encoding by using external decoders. See [[XML Decoders]] for more details.
647+
648+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 (**without** BOM - Byte Order Marker) or UTF-16 (UTF-16 BOM).
649+
650+TXMLDocument has an encoding property since FPC 2.4. It is ignored as WriteXMLFile always uses UTF-8.
651+
652+- FPC 2.4 doesn´t generate an encoding attribute in the first line of the XML file
653+- FPC 2.6.0 and later explicitly write an UTF8 encoding attribute, as this is needed for some programs that cannot handle the XML without it.
654+
655+## See also
656+
657+- [[XML Decoders]]
658+- [[Using INI Files]]
659+- [[fcl-xml]]
660+- [[Internet Tools]], for XPath 2 / XQuery processing
661+
662+## External Links
663+
664+- [W3Schools](http://www.w3schools.com/xml/default.asp) Xml Tutorial
665+
666+- [Thomas Zastrow article](http://www.thomas-zastrow.de/texte/fpcxml/index.php) [Alternate link](http://web.archive.org/web/20080802150722/http://www.thomas-zastrow.de/texte/fpcxml/index.php) FPC and XML
667+
668+---
669+*From the Free Pascal wiki, page "XML Tutorial", as it stood on 2023-07-05 after a change by Wp, 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).*

Back to history