# XML Documents

> Load XML files with DTD and CSS into ServerTextControl, read and change elements with GetXmlElements and XmlElement, handle XmlInvalid and save XML and CSS.

- **Product:** TX Text Control .NET Server
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET Server > Document Processing > XML Documents
- **5032 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/xml/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/xml/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/xml/llms-full.txt

---

An XML file such as an address list holds structured data and no formatting. `ServerTextControl` loads it together with its document type definition (DTD), which says which elements are allowed where, and a CSS stylesheet, which says how each element looks. Your code then reads and changes the elements by name, and every change is checked against the DTD. Read on for the files involved, the rules for loading and saving and the classes that give access to the elements.

XML as a data source for a mail merge is a different subject, covered in [Data Sources](https://docs.textcontrol.com/tx-text-control-dotnet-server/mail-merge/data-sources/llms-full.txt). Events of `ServerTextControl` in general are listed in [Events](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/events/llms-full.txt).

### Requirements

XML documents need the Classic engine. The XML filter is part of the Classic package `TXTextControl.TextControl.ASP.SDK`, which runs on Windows only; the Core package `TXTextControl.TextControl.Core.SDK` doesn't contain it, just as it doesn't contain the filter for DOC files. [Create Your First Application](https://docs.textcontrol.com/tx-text-control-dotnet-server/getting-started/document-processing/first-application/#choose-core-or-classic) compares the two packages. The examples are console programs that use the Classic package.

### The XML, DTD and CSS Files

The examples on this page use an address list. Save the three files below in a *Documents* folder of your project and set their **Copy to Output Directory** property to **Copy if newer**.

*addresses.xml* names its stylesheet and its DTD in its first lines:

```xml
<?xml version="1.0"?>
<?XML:stylesheet type="text/css" href="addresses.css"?>

<Addresses>
  <DocumentHeadline>Addresses</DocumentHeadline>
  <AddressTable>
    <Address>
      <Name>Jack Miller</Name>
      <Street>Baker Street</Street>
      <City>New York</City>
    </Address>
    <Address>
      <Name>Tom Baker</Name>
      <Street>Miller Street</Street>
      <City>Los Angeles</City>
    </Address>
    <Address>
      <Name>Bill Taylor</Name>
      <Street>Old Street</Street>
      <City>Washington</City>
    </Address>
  </AddressTable>
</Addresses>
```

*addresses.dtd* allows one headline and one address table with at least one address. Each address has a name, a street, a city and an optional telephone number:

```text
<!ELEMENT Addresses (DocumentHeadline, AddressTable)>
<!ELEMENT DocumentHeadline (#PCDATA)>
<!ELEMENT AddressTable (Address+)>
<!ELEMENT Address (Name, Street, City, Telephone?)>
<!ELEMENT Name (#PCDATA)>
<!ELEMENT Street (#PCDATA)>
<!ELEMENT City (#PCDATA)>
<!ELEMENT Telephone (#PCDATA)>
```

Only elements declared with `#PCDATA` contain text: `DocumentHeadline`, `Name`, `Street`, `City` and `Telephone`. `Addresses`, `AddressTable` and `Address` only give the document its structure.

*addresses.css* formats the elements:

```css
DocumentHeadline { display: block; font-size: 16pt; color: blue }
Name { display: block; font-size: 12pt }
Street, City, Telephone { display: block; font-size: 11pt }
```

### Load and Save an XML Document

[ServerTextControl.Load](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/servertextcontrol/load-method/llms-full.txt) with the [StreamType](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/streamtype/enumeration/llms-full.txt) value `XMLFormat` loads the XML file, checks it against its DTD and applies the styles of the CSS file. The following program loads the address list, reports errors and saves the document again:

```csharp
using TXTextControl;

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();

    // Report documents that break the DTD or aren't XML
    tx.XmlInvalid += (sender, e) =>
        Console.WriteLine($"Invalid: {e.Reason} (line {e.LineNumber}, position {e.LinePosition})");
    tx.XmlNotWellFormed += (sender, e) =>
        Console.WriteLine($"Not well-formed: {e.Reason} (line {e.LineNumber}, position {e.LinePosition})");

    tx.Load("Documents/addresses.xml", StreamType.XMLFormat);
    Console.WriteLine(tx.Text);

    // The text goes to the XML file, the styles to the CSS file
    tx.Save("Documents/addresses.xml", StreamType.XMLFormat);
    tx.Save("Documents/addresses.css", StreamType.CascadingStylesheet);
}
```

What happens on loading depends on the file:

- **Valid.** The document matches its DTD and is loaded with the formatting of its CSS file.
- **Invalid.** The document breaks a rule of the DTD. [XmlInvalid](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/servertextcontrol/xmlinvalid-event/llms-full.txt) occurs, and the text and the styles are still loaded.
- **Not well-formed.** The file contains invalid XML syntax. [XmlNotWellFormed](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/servertextcontrol/xmlnotwellformed-event/llms-full.txt) occurs, and the document isn't loaded.

Saving has three rules:

- **Two calls of `Save`.** `Save` with `XMLFormat` writes the text and the elements to the XML file, and a second call with `CascadingStylesheet` writes the styles to the CSS file. Without the second call, changes of the formatting are lost.
- **The DTD stays as it is.** Changing and saving the DTD isn't supported, and neither is creating a new XML document from scratch: start from an existing XML file with its DTD.
- **No partial loading and saving.** [Selection.Load](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/selection/load-method/llms-full.txt) and [Selection.Save](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/selection/save-method/llms-full.txt) can't be used with XML, because an XML document inserted into another document, or a part of one, would no longer be valid.

An XML document has no direct formatting: its formatting comes from the styles of the CSS file, which are in the [InlineStyles](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/servertextcontrol/inlinestyles-property/llms-full.txt) and [ParagraphStyles](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/servertextcontrol/paragraphstyles-property/llms-full.txt) collections after loading. Change a style there and save the CSS file to keep the change.

### Handle XML Errors

Both events pass an [XmlErrorEventArgs](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlerroreventargs/llms-full.txt) object:

| Property | Contains |
|---|---|
| [Reason](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlerroreventargs/reason-property/llms-full.txt) | The reason for the error |
| [LineNumber](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlerroreventargs/linenumber-property/llms-full.txt) | The line of the XML document that contains the error, or 0 if it isn't known |
| [LinePosition](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlerroreventargs/lineposition-property/llms-full.txt) | The character position of the error within the line, or 0 |
| [FilePosition](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlerroreventargs/fileposition-property/llms-full.txt) | The absolute position of the error in the file, or 0 |
| [URL](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlerroreventargs/url-property/llms-full.txt) | The URL of the document that contains the error, or an empty string |

`XmlInvalid` doesn't only occur on loading. It also occurs when your code changes the document in a way the DTD doesn't allow, as described in [Insert and Remove Elements](#insert-and-remove-elements).

### Read the Elements of a Document

[ServerTextControl.GetXmlElements](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/servertextcontrol/getxmlelements-method/llms-full.txt) returns an [XmlElementCollection](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelementcollection/llms-full.txt) with all elements of one name, as [XmlElement](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelement/llms-full.txt) objects. There is one collection for each element name: one for all `Address` elements, another for all `Name` elements. The following program reads the address list in three ways:

```csharp
using TXTextControl;

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();
    tx.Load("Documents/addresses.xml", StreamType.XMLFormat);

    // All Name elements, in the order of the document
    foreach (XmlElement name in tx.GetXmlElements("Name"))
    {
        Console.WriteLine($"{name.ElementName}: {name.Text}");
    }

    // The same with an index, which starts at 1
    XmlElementCollection names = tx.GetXmlElements("Name");

    for (int i = 1; i <= names.Count; i++)
    {
        Console.WriteLine($"Name {i} of {names.Count}: {names.GetItem(i).Text}");
    }

    // An element with sub-elements returns their texts, separated by line feeds
    Console.WriteLine(tx.GetXmlElements("Address").GetItem(2).Text);
}
```

The last line prints the three sub-elements of the second address on three lines:

```text
Tom Baker
Miller Street
Los Angeles
```

The code works as follows:

- [GetItem](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelementcollection/getitem-method/llms-full.txt) takes a one-based index and returns `null` if there is no element with it. Without a parameter, it returns the element at the current input position.
- [Count](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelementcollection/count-property/llms-full.txt) is the number of elements in the collection.
- [ElementName](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelement/elementname-property/llms-full.txt) is the name of the element.
- [Text](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelement/text-property/llms-full.txt) is the text of the element. For an element with sub-elements, it joins their texts with line feed characters (character 10, `\n` in C#).

### Change the Text of Elements

Setting `Text` replaces the text of an element. For an element with sub-elements, separate the texts of the sub-elements with `\n`, in the order of the DTD:

```csharp
using TXTextControl;

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();
    tx.Load("Documents/addresses.xml", StreamType.XMLFormat);

    // Replace the second address: name, street and city
    tx.GetXmlElements("Address").GetItem(2).Text = "Paul Baskerville\nJackson Street\nLondon";

    // Change a single text element
    tx.GetXmlElements("DocumentHeadline").GetItem(1).Text = "Customer Addresses";

    tx.Save("Documents/addresses.xml", StreamType.XMLFormat);
}
```

[XmlElement.Select](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelement/select-method/llms-full.txt) selects the text of an element that belongs to the document and returns `true` if it could be selected. For an element with sub-elements, it selects the text of the first sub-element. The selected text is then available through `tx.Selection`, as for any other document.

### Insert and Remove Elements

Elements are added and removed through the collection of their name, and each change is checked against the DTD. With *addresses.dtd*, only complete `Address` elements can be added to or removed from the address table, and a `Telephone` element can be added to an address that has none. A change the DTD doesn't allow isn't made: the method returns `false`, and `XmlInvalid` occurs. The DTD requires at least one address, so removing the last one fails, for example.

```csharp
using TXTextControl;

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();
    tx.XmlInvalid += (sender, e) => Console.WriteLine($"Not allowed: {e.Reason}");
    tx.Load("Documents/addresses.xml", StreamType.XMLFormat);

    XmlElementCollection addresses = tx.GetXmlElements("Address");

    // Insert a new address after the second one
    XmlElement newAddress = new XmlElement("Address", "Paul Baskerville\nJackson Street\nLondon");
    bool added = addresses.Add(newAddress, -1, 2);
    Console.WriteLine($"Address added: {added}, addresses: {tx.GetXmlElements("Address").Count}");

    // Give the first address a telephone number
    XmlElement firstAddress = tx.GetXmlElements("Address").GetItem(1);
    firstAddress.AddChild(new XmlElement("Telephone", "555 0100"));
    Console.WriteLine($"Telephone: {firstAddress.GetChildItem("Telephone").Text}");

    // Remove the telephone number and the address inserted above
    firstAddress.RemoveChild("Telephone");
    bool removed = tx.GetXmlElements("Address").Remove(3);
    Console.WriteLine($"Address removed: {removed}");

    tx.Save("Documents/addresses.xml", StreamType.XMLFormat);
}
```

The code works as follows:

- The [XmlElement constructor](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelement/constructor/llms-full.txt) takes the name of the element and, optionally, its text. The text of an element with sub-elements is separated by `\n`, as for `Text`.
- [XmlElementCollection.Add](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelementcollection/add-method/llms-full.txt) takes the element and two one-based positions in the collection, `before` and `after`. `Add(newAddress, -1, 2)` places the new address after the second one: -1 means that a parameter is ignored, and 0 places the element before or after the element at the current input position.
- [AddChild](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelement/addchild-method/llms-full.txt) adds a sub-element at the position the DTD gives it. Where an element can have several sub-elements of the same name, a second overload, `AddChild(element, before, after)`, places it among them.
- [GetChildItem](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelement/getchilditem-method/llms-full.txt) returns the first sub-element of a name, or, with a one-based index as the second parameter, a particular one. [RemoveChild](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelement/removechild-method/llms-full.txt) takes the same parameters and returns `false` if the DTD doesn't allow the removal.
- [XmlElementCollection.Remove](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelementcollection/remove-method/llms-full.txt) removes the element with a one-based index; 0 removes the element at the current input position.

Two properties of `XmlElement` control what happens when a new element is added: with [AutoText](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelement/autotext-property/llms-full.txt) set to `true`, an element without text gets its name in square brackets as its text, such as `[Telephone]`, and with [AutoSelect](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/xmlelement/autoselect-property/llms-full.txt) set to `true`, the text of the new element is selected.

### Next Steps

- [Loading Documents](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/loading/llms-full.txt): load documents of the other formats and handle filter errors.
- [Saving and Converting Documents](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/saving-converting/llms-full.txt): save documents in other formats, such as PDF.
- [Paragraphs](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/paragraphs/llms-full.txt): work with the paragraph styles that format the elements.
- [Events](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/events/llms-full.txt): the other events of `ServerTextControl`.
- [Supported File Formats](https://docs.textcontrol.com/tx-text-control-dotnet-server/overview/file-formats/llms-full.txt): the formats and their `StreamType` values.

---

## Nearby

- **Previous:** [Conditional Instructions](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/conditional-instructions/llms-full.txt)
- **Next:** [Server Visualization](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/server-visualization/llms-full.txt)
