cXml class

A cXml holds one XML document. It reads the document from a string or a file, or starts a new one with a root element. It finds, reads and changes elements and attributes by a path such as library/book, and writes the document back as a string or a file. The elements, text and comments of the document are cXmlNode objects, which the Document and RootElement properties and the search methods return.

An object is created with Dim x As New cXml. A new object holds no document. Parse, LoadFromFile, CreateNew or the XmlString property gives it one, and until then the members that take a path find nothing.

Dim x As New cXml
x.Parse "<library><book id=""1"">Dune</book><book id=""2"">Emma</book></library>"

Debug.Print x.GetElementText("library/book")
Debug.Print x.GetAttribute("library/book", "id")
Debug.Print x.SelectElements("book").Count

x.AddElement "library", "note"
x.SetElementText "library/note", "checked"
Debug.Print x.ToXmlString(False)
' Output:
' Dune
' 1
' 2
' <library><book id="1">Dune</book><book id="2">Emma</book><note>checked</note></library>

Paths

The members that take an xpath parameter find an element by a path: SelectElement, GetElementText, SetElementText, GetAttribute, SetAttribute, RemoveAttribute and RemoveElement, and AddElement for its parentXPath. The name of the parameter is misleading: the syntax is a small one of the package’s own and is not XPath.

  • A path is a list of names separated by /, such as library/book. The search starts at the document node, so the first name is the name of the root element: library/book finds a book, and book finds nothing.
  • Each name selects the first child, of the node found so far, whose name equals it. Letter case is ignored. Only direct children count, and when several children have the name, the first one is taken.
  • White space around a name is removed, and an empty name is ignored. /library//book and library / book both mean library/book.
  • A name is compared as a whole, so a namespace prefix is part of it: ns:item is the name of an element written <ns:item>. The package does no namespace processing.
  • Nothing else has a meaning. A position (book[2]), an attribute (@id), *, . and .. are names that no element has, so the path selects nothing. //book does not search at any depth: the empty name is ignored, and the path means book. SelectElements searches at any depth, and the ChildNodes of a node reach the other children.
  • The nodes that are not elements have names too, #text, #comment and #cdata-section, and a path can select them: library/#comment is the first comment directly inside the root element.
  • A path with no name, an empty string or /, selects nothing, and the Debug Console shows SelectElement failed: Subscript out of range.
Dim x As New cXml
x.Parse "<library><book id=""1"">Dune</book><book id=""2"">Emma</book></library>"

Debug.Print "[" & x.GetElementText("book") & "]"
Debug.Print "[" & x.GetElementText("library/book") & "]"
Debug.Print "[" & x.GetElementText("/LIBRARY/Book") & "]"
Debug.Print "[" & x.GetElementText(" library // book ") & "]"
Debug.Print "[" & x.GetElementText("library/book[2]") & "]"
' Output:
' []
' [Dune]
' [Dune]
' [Dune]
' []

To reach the second book, SelectElements returns every book, or the ChildNodes of the parent can be searched:

Dim x As New cXml
x.Parse "<library><book id=""1"">Dune</book><book id=""2"">Emma</book></library>"

Dim books As Collection
Set books = x.SelectElements("book")
Debug.Print books(2).GetAttribute("id") & " " & books(2).ChildNodes(1).Value

Dim children As Collection
Set children = x.RootElement.GetChildNodesByName("book")
Debug.Print children(2).GetAttribute("id")
' Output:
' 2 Emma
' 2

Reporting failures

A member that cannot do its work returns a value that says so, and does not stop the program. Parse, LoadFromFile, SaveToFile, Validate and RemoveElement return False. SelectElement and AddElement return Nothing. GetElementText and GetAttribute return an empty string, and SelectElements an empty collection. A method that returns nothing, such as SetAttribute, does nothing.

When an error causes the failure, the member also:

  • writes a line to the Debug Console that names the member and gives the error’s description, such as Parse failed: XML string cannot be empty or SelectElement failed: Document not initialized. In a built executable, Debug.Print writes nothing (see Debug), so the line appears in the IDE only;
  • leaves the error in the Err object. After the call returns, Err.Number, Err.Source and Err.Description describe it. A program reads them straight after the call, because the next call of most members clears them, and calls Err.Clear before the call so that an earlier error does not remain in them.

The errors that occur are ERR_EMPTY_DOCUMENT, which Parse raises for an empty string and SelectElement for a document that has not been read or created, and the errors of the Visual Basic runtime that the code meets while it works: 5 (Invalid procedure call or argument) for a tag cut off by the end of the text, 9 (Subscript out of range) for a path with no name, and 91 (Object variable or With block variable not set) in RemoveElement for a path that selects nothing. The other error constants of mXml are not used.

The members that let an error reach the program are XmlString, ToXmlString and Merge.

Dim x As New cXml

Err.Clear
Debug.Print x.Parse("")
Debug.Print Err.Number = ERR_EMPTY_DOCUMENT
Debug.Print Err.Source & ": " & Err.Description

Err.Clear
Debug.Print (x.SelectElement("library") Is Nothing)
Debug.Print Err.Number = ERR_EMPTY_DOCUMENT
' Output:
' Parse failed: XML string cannot be empty
' False
' True
' cXml.Parse: XML string cannot be empty
' SelectElement failed: Document not initialized
' True
' True

Properties

Document

Returns the document node.

Syntax: object.Document As cXmlNode

object
required An object expression that evaluates to a cXml.

Read-only. The node has the NodeType Document and the Name #document. Its ChildNodes hold the root element and any comments and text outside it, in document order, including text that is only white space. A new object holds an empty document node. Parse, LoadFromFile, CreateNew and XmlString replace it.

Dim x As New cXml
x.Parse "<!-- top --><r></r>"

Debug.Print x.Document.Name & " " & x.Document.NodeType
Dim n As cXmlNode
For Each n In x.Document.ChildNodes
    Debug.Print n.Name
Next
' Output:
' #document 9
' #comment
' r

Encoding

Returns or sets the encoding name that the XML declaration states.

Syntax: object.Encoding [ = value ]

object
required An object expression that evaluates to a cXml.

A String. The default is UTF-8, the value of DEFAULT_ENCODING. ToXmlString and SaveToFile write it into the declaration. Parse and LoadFromFile set it from the encoding of the declaration of the text they read, when the declaration has one written with double quotes. Without one, the property keeps the value it had, which is the value of an earlier document if the object read one. CreateNew leaves it as it is.

The property is a label. SaveToFile always writes UTF-8 and LoadFromFile always reads UTF-8, whatever the label says.

Dim x As New cXml
Debug.Print x.Encoding

x.Parse "<?xml version=""1.0"" encoding=""ISO-8859-1""?><r></r>"
Debug.Print x.Encoding

x.Encoding = "UTF-16"
Debug.Print x.ToXmlString()
' Output:
' UTF-8
' ISO-8859-1
' <?xml version="1.0" encoding="UTF-16"?><r/>

Initialized

Returns whether the object holds a document.

Syntax: object.Initialized As Boolean

object
required An object expression that evaluates to a cXml.

Read-only. It is False for a new object. It becomes True when Parse, LoadFromFile or XmlString reads text, even text with no element in it, and when CreateNew creates a document, and Clone and Merge can set it. A Parse that fails leaves it as it was.

While it is False, SelectElement returns Nothing, and the members built on it find nothing. SelectElements returns an empty collection, and Validate returns False.

Dim x As New cXml
Debug.Print x.Initialized
x.CreateNew "r"
Debug.Print x.Initialized
' Output:
' False
' True

RootElement

Returns the root element.

Syntax: object.RootElement As cXmlNode

object
required An object expression that evaluates to a cXml.

Read-only. It returns the first element directly inside the document node, or Nothing for a new object and for text that holds no element. Parse, LoadFromFile, XmlString, CreateNew and Clone set it. Nothing else changes it, so it can name an element that is no longer in the document; see Clone, Merge and the root element.

Dim x As New cXml
Debug.Print (x.RootElement Is Nothing)

x.Parse "<a><b>1</b></a>"
Debug.Print x.RootElement.Name
Debug.Print x.RootElement.ChildNodes.Count
' Output:
' True
' a
' 1

Version

Returns or sets the XML version that the XML declaration states.

Syntax: object.Version [ = value ]

object
required An object expression that evaluates to a cXml.

A String. The default is 1.0, the value of DEFAULT_XML_VERSION. ToXmlString and SaveToFile write it into the declaration. Parse and LoadFromFile set it from the version of the declaration of the text they read, when the declaration has one written with double quotes; otherwise the property keeps the value it had. The package does not check the value.

Dim x As New cXml
x.Parse "<?xml version=""1.1""?><r></r>"
Debug.Print x.Version
Debug.Print x.ToXmlString()
' Output:
' 1.1
' <?xml version="1.1" encoding="UTF-8"?><r/>

XmlString

Returns or sets the text of the document that was last read.

Syntax: object.XmlString [ = value ]

object
required An object expression that evaluates to a cXml.

A String. Assigning to it reads the text as Parse does and replaces the document. Reading it returns the text that Parse, LoadFromFile or an assignment last read. It is not made again from the document, so it does not show later changes: ToXmlString does. Clone does not copy it.

Assigning an empty string is not an error, unlike Parse with an empty string: it gives an initialised document with no nodes.

Note

Assigning text that the parser cannot read raises an error, where Parse returns False for the same text. Assigning < raises error 5, Invalid procedure call or argument. The text is kept, so reading the property returns it.

Dim x As New cXml
x.XmlString = "<r><a>1</a></r>"
Debug.Print x.GetElementText("r/a")

x.SetElementText "r/a", "2"
Debug.Print x.XmlString
Debug.Print x.ToXmlString(False)
' Output:
' 1
' <r><a>1</a></r>
' <r><a>2</a></r>

Methods

New

Creates a cXml object.

Syntax: New cXml

The constructor takes no arguments. The new object has an empty document node, the Version 1.0 and the Encoding UTF-8, and Initialized is False.

Dim x As New cXml
Debug.Print x.Initialized & " " & x.Version & " " & x.Encoding
' Output:
' False 1.0 UTF-8

AddElement

Adds an empty element as the last child of an element.

Syntax: object.AddElement ( parentXPath , elementName ) As cXmlNode

object
required An object expression that evaluates to a cXml.
parentXPath
required A String, the path of the element that receives the new one.
elementName
required A String, the name of the new element.

Returns the new cXmlNode, whose Parent is the element found. Returns Nothing, and adds nothing, when the path selects nothing. A document has one root element, which CreateNew or the text read gives it, so there is no way to add a second.

The name is not checked. An empty name or a name with a space is accepted, and Validate rejects an empty one. The new element has no text. SetElementText gives it some, by the path of the new element, which is the path of the parent and the name; when several siblings have the name, the path reaches the first. A program that holds the returned node can instead add a text node to its ChildNodes (see ChildNodes).

Dim x As New cXml
x.CreateNew "library"

Dim book As cXmlNode
Set book = x.AddElement("library", "book")
book.SetAttribute "id", "1"
x.SetElementText "library/book", "Dune"

Debug.Print (x.AddElement("library/shelf", "book") Is Nothing)
Debug.Print x.ToXmlString(False)
' Output:
' True
' <library><book id="1">Dune</book></library>

Clone

Returns a copy of the document.

Syntax: object.Clone ( ) As cXml

object
required An object expression that evaluates to a cXml.

Returns a new cXml with its own copy of every node, so that a change to one document does not reach the other. The copy has the same Version and Encoding, and is initialised when the original is. For an object that holds no document, the result holds none either. The XmlString of the copy is empty.

The copied nodes have no Parent, which stops RemoveElement from removing them; see Clone, Merge and the root element.

Dim x As New cXml
x.Parse "<r><a>1</a></r>"

Dim y As cXml
Set y = x.Clone()
y.SetElementText "r/a", "2"

Debug.Print x.GetElementText("r/a") & " " & y.GetElementText("r/a")
' Output:
' 1 2

CreateNew

Replaces the document with a new one that holds only a root element.

Syntax: object.CreateNew ( rootElementName )

object
required An object expression that evaluates to a cXml.
rootElementName
required A String, the name of the root element. It is not checked.

Discards the document and creates an empty root element with that name. Initialized becomes True. Version, Encoding and XmlString keep the values they had.

Dim x As New cXml
x.CreateNew "config"
Debug.Print x.ToXmlString()
' Output:
' <?xml version="1.0" encoding="UTF-8"?><config/>

GetAttribute

Returns an attribute of an element.

Syntax: object.GetAttribute ( xpath , attrName ) As String

object
required An object expression that evaluates to a cXml.
xpath
required A String, the path of the element.
attrName
required A String, the name of the attribute. Letter case is ignored.

Returns the value of the attribute, as GetAttribute of the element’s node does. Returns an empty string when the path selects nothing or the element has no such attribute, which a program cannot tell from an attribute whose value is empty.

Dim x As New cXml
x.Parse "<r><a id=""7"" empty=""""></a></r>"

Debug.Print "[" & x.GetAttribute("r/a", "id") & "]"
Debug.Print "[" & x.GetAttribute("r/a", "ID") & "]"
Debug.Print "[" & x.GetAttribute("r/a", "empty") & "]"
Debug.Print "[" & x.GetAttribute("r/a", "missing") & "]"
Debug.Print "[" & x.GetAttribute("r/nothing", "id") & "]"
' Output:
' [7]
' [7]
' []
' []
' []

GetElementText

Returns the text inside an element.

Syntax: object.GetElementText ( xpath ) As String

object
required An object expression that evaluates to a cXml.
xpath
required A String, the path of the element.

Returns the text of the element: the values of all the text nodes and CDATA sections inside it, at any depth, one after the other in document order with nothing between them. Comments add nothing. Text that is only white space is included, so the text of an element in an indented document holds the line breaks and the indentation. Entities in the text are returned as written; see Entities.

Returns an empty string when the path selects nothing, which a program cannot tell from an element with no text.

Dim x As New cXml
x.Parse "<p>Hello <b>big</b> world<!-- c --><![CDATA[ & more]]></p>"

Debug.Print "[" & x.GetElementText("p") & "]"
Debug.Print "[" & x.GetElementText("p/b") & "]"
Debug.Print "[" & x.GetElementText("p/i") & "]"

x.Parse "<r>" & vbCrLf & "  <a>1</a>" & vbCrLf & "</r>"
Debug.Print Replace(Replace(x.GetElementText("r"), " ", "."), vbCrLf, "|")
' Output:
' [Hello big world & more]
' [big]
' []
' |..1|

LoadFromFile

Reads a document from a file.

Syntax: object.LoadFromFile ( filePath ) As Boolean

object
required An object expression that evaluates to a cXml.
filePath
required A String, the path of the file.

Reads the whole file, removes a UTF-8 byte order mark if the file starts with one, converts the bytes as UTF-8 with UTF8BytesToString and reads the text as Parse does. Returns True when the file was read.

Returns False, and writes LoadFromFile failed: and the description to the Debug Console, when the file cannot be opened, is empty, or has fewer than three bytes. It returns True when the file was read but the text then fails to parse, because it ignores the result of Parse; see Files. The file is read as UTF-8 whatever its declaration says, and the declaration’s encoding only sets Encoding.

Dim path As String
path = Environ$("TEMP") & "\tbxml_loadfromfile.xml"

Dim f As Long
f = FreeFile
Open path For Output As #f
Print #f, "<?xml version=""1.0"" encoding=""UTF-8""?>"
Print #f, "<config><name>demo</name></config>"
Close #f

Dim x As New cXml
Debug.Print x.LoadFromFile(path)
Debug.Print x.GetElementText("config/name")
Kill path
' Output:
' True
' demo

Merge

Appends the content of another document to this one.

Syntax: object.Merge ( otherXml )

object
required An object expression that evaluates to a cXml.
otherXml
required A cXml, the document to copy from. It is not changed.

Appends a copy of every child of the root element of otherXml, text and comments included, to the end of the root element of this document. The names and attributes of the two root elements are not compared or merged, and this document keeps its own. Nothing happens when otherXml holds no document.

When this object holds no document, it becomes a copy of the whole of otherXml, and Initialized becomes True. Its RootElement stays Nothing, so a second Merge into it does nothing.

The copies have no Parent; see Clone, Merge and the root element.

Note

Merge with Nothing as otherXml raises error 91, Object variable or With block variable not set, because the code tests the object and its property in one expression.

Dim a As New cXml
Dim b As New cXml
a.Parse "<list a=""1""><x>1</x></list>"
b.Parse "<other b=""2""><y>2</y></other>"

a.Merge b
Debug.Print a.ToXmlString(False)
Debug.Print b.ToXmlString(False)
' Output:
' <list a="1"><x>1</x><y>2</y></list>
' <other b="2"><y>2</y></other>

Parse

Reads a document from a string.

Syntax: object.Parse ( xml ) As Boolean

object
required An object expression that evaluates to a cXml.
xml
required A String, the text to read.

Replaces the document with the one in the text, sets RootElement and XmlString, and returns True. The version and encoding of an XML declaration, written with double quotes, set Version and Encoding.

The parser builds these nodes: elements with their attributes, text, comments and CDATA sections. Text that is only white space is kept, in the document and inside elements. An end tag closes the innermost open element whatever its name is, and an element still open at the end of the text is closed by it. Processing instructions and the declaration are not kept.

Returns False, and writes Parse failed: and the description to the Debug Console, for an empty string and for a tag that the end of the text cuts off, such as < or <a. Any other text gives True: the parser does not check that the text is well formed XML, so text that is not XML, an unclosed element and an end tag that names another element all succeed. Validate detects almost none of them. A Parse that fails part-way has already replaced the document with what it had read; see Malformed text and failed parses.

For text that holds a comment or a CDATA section with no end, or a DOCTYPE declaration, Parse may never return; see Comments, CDATA sections and declarations.

Dim x As New cXml
Debug.Print x.Parse("<?xml version=""1.1"" encoding=""ISO-8859-1""?><r a=""1""><b>text</b></r>")
Debug.Print x.Version & " " & x.Encoding & " " & x.RootElement.Name

Debug.Print x.Parse("")
Debug.Print x.Parse("not xml")
Debug.Print x.Parse("<a><b></a>")
Debug.Print x.Parse("<r")
' Output:
' True
' 1.1 ISO-8859-1 r
' Parse failed: XML string cannot be empty
' False
' True
' True
' Parse failed: Invalid procedure call or argument
' False

RemoveAttribute

Removes an attribute from an element.

Syntax: object.RemoveAttribute ( xpath , attrName )

object
required An object expression that evaluates to a cXml.
xpath
required A String, the path of the element.
attrName
required A String, the name of the attribute. Letter case is ignored.

Removes the first attribute whose name matches, with its value. Nothing happens, and no error is raised, when the path selects nothing or the element has no such attribute.

Dim x As New cXml
x.Parse "<r a=""1"" b=""2""></r>"

x.RemoveAttribute "r", "A"
x.RemoveAttribute "r", "missing"
x.RemoveAttribute "nothing", "b"
Debug.Print x.ToXmlString(False)
' Output:
' <r b="2"/>

RemoveElement

Removes an element, with everything inside it.

Syntax: object.RemoveElement ( xpath ) As Boolean

object
required An object expression that evaluates to a cXml.
xpath
required A String, the path of the element.

Takes the first element that the path selects out of the ChildNodes of its parent, and returns True. Returns False when the path selects nothing, and for a node that has no Parent, which includes the nodes that Clone and Merge copy. In the first case the Debug Console shows RemoveElement failed: Object variable or With block variable not set.

Removing the root element leaves RootElement as it was; see Clone, Merge and the root element.

Dim x As New cXml
x.Parse "<r><a>1</a><a>2</a><b></b></r>"

Debug.Print x.RemoveElement("r/a")
Debug.Print x.GetElementText("r/a")
Debug.Print x.RemoveElement("r/missing")
' Output:
' True
' 2
' RemoveElement failed: Object variable or With block variable not set
' False

SaveToFile

Writes the document to a file.

Syntax: object.SaveToFile ( filePath [ , withDeclaration ] ) As Boolean

object
required An object expression that evaluates to a cXml.
filePath
required A String, the path of the file. An existing file is deleted first.
withDeclaration
optional A Boolean. True (the default) writes the XML declaration before the document.

Writes the three bytes of a UTF-8 byte order mark, then the document as ToXmlString returns it with withDeclaration and no other option, converted with StringToUTF8Bytes. The text is never indented. Returns True when the file was written, and False, with SaveToFile failed: and the description in the Debug Console, when it could not be, for example because the folder does not exist.

The file is UTF-8 whatever Encoding says, and the declaration states that value. A document with no content written without a declaration is the byte order mark followed by one zero byte.

Dim path As String
path = Environ$("TEMP") & "\tbxml_savetofile.xml"

Dim x As New cXml
x.CreateNew "note"
x.AddElement "note", "to"
x.SetElementText "note/to", "Ada"
Debug.Print x.SaveToFile(path)

Dim f As Long
f = FreeFile
Open path For Binary Access Read As #f
Dim head(0 To 2) As Byte
Get #f, , head
Close #f
Debug.Print Hex$(head(0)) & " " & Hex$(head(1)) & " " & Hex$(head(2))

Dim y As New cXml
Debug.Print y.LoadFromFile(path)
Debug.Print y.GetElementText("note/to")
Kill path
' Output:
' True
' EF BB BF
' True
' Ada

SelectElement

Returns the element that a path selects.

Syntax: object.SelectElement ( xpath ) As cXmlNode

object
required An object expression that evaluates to a cXml.
xpath
required A String, the path of the element.

Returns the cXmlNode that the path selects, or Nothing when it selects nothing. It also returns Nothing when Initialized is False and when the path holds no name, and in both cases the Debug Console shows a line that begins SelectElement failed:.

The other members that take a path call this one.

Dim x As New cXml
x.Parse "<library><book id=""1"">Dune</book><book id=""2"">Emma</book></library>"

Dim n As cXmlNode
Set n = x.SelectElement("library/book")
Debug.Print n.GetAttribute("id") & " " & n.ChildNodes(1).Value
Debug.Print (x.SelectElement("library/magazine") Is Nothing)
' Output:
' 1 Dune
' True

SelectElements

Returns every node with a given name.

Syntax: object.SelectElements ( tagName ) As Collection

object
required An object expression that evaluates to a cXml.
tagName
required A String, one name and not a path. Letter case is ignored.

Returns a new Collection of the cXmlNode objects, at any depth in the document, whose Name matches, in document order. An element inside another element of the same name is returned too. The collection is empty when nothing matches and when Initialized is False; the method never returns Nothing.

The names of the nodes that are not elements match, so #text returns every text node. The method does not read paths: r/a matches nothing.

Dim x As New cXml
x.Parse "<r><a>1</a><b><a>2</a></b><A>3</A></r>"

Dim found As Collection
Set found = x.SelectElements("a")
Debug.Print found.Count

Dim n As cXmlNode
For Each n In found
    Debug.Print n.Name & " " & n.ChildNodes(1).Value
Next
Debug.Print x.SelectElements("r/a").Count
' Output:
' 3
' a 1
' a 2
' A 3
' 0

SetAttribute

Sets an attribute of an element.

Syntax: object.SetAttribute ( xpath , attrName , attrValue )

object
required An object expression that evaluates to a cXml.
xpath
required A String, the path of the element.
attrName
required A String, the name of the attribute.
attrValue
required A String, the value.

Adds the attribute, or replaces the one with the same name, as SetAttribute of the element’s node does: a replaced attribute moves to the end of the element’s attributes. Nothing happens, and no error is raised, when the path selects nothing.

Dim x As New cXml
x.CreateNew "r"

x.SetAttribute "r", "a", "1"
x.SetAttribute "r", "b", "2"
x.SetAttribute "r", "a", "3"
x.SetAttribute "r/missing", "z", "1"
Debug.Print x.ToXmlString(False)
' Output:
' <r b="2" a="3"/>

SetElementText

Replaces the text of an element.

Syntax: object.SetElementText ( xpath , text )

object
required An object expression that evaluates to a cXml.
xpath
required A String, the path of the element.
text
required A String, the new text, as plain text: ToXmlString escapes it when it writes it.

Removes the text nodes directly inside the element and adds one text node that holds text, after the element’s other children. The child elements, comments and CDATA sections stay, so the text moves to the end of an element with mixed content. Setting an empty string leaves an empty text node, which makes ToXmlString write <c></c> and not <c/>. Nothing happens, and no error is raised, when the path selects nothing.

Dim x As New cXml
x.Parse "<r><a>one<b>bee</b>two</a></r>"

x.SetElementText "r/a", "NEW"
x.SetElementText "r/missing", "lost"
Debug.Print x.ToXmlString(False)
' Output:
' <r><a><b>bee</b>NEW</a></r>

ToXmlString

Returns the document as text.

Syntax: object.ToXmlString ( [ withDeclaration [ , prettyPrint [ , indent ] ] ] ) As String

object
required An object expression that evaluates to a cXml.
withDeclaration
optional A Boolean. True (the default) starts the text with a declaration such as <?xml version="1.0" encoding="UTF-8"?>, written from Version and Encoding, with no line break after it.
prettyPrint
optional A Boolean. False (the default) writes the document compactly: nothing is added between the nodes. True puts each element, comment, CDATA section and text on a line of its own, ends every line with a carriage return and a line feed, and leaves out the text nodes that are only white space. See Pretty printing for what the indentation looks like.
indent
optional A String, the indentation for each level of nesting. The default is two spaces. Only the first character of the string is used.

The text holds elements, text, comments and CDATA sections. An element with no child is written as <name/>, and an element with children as <name>...</name>. The characters &, <, >, " and ' are replaced by &amp;, &lt;, &gt;, &quot; and &apos; in text and in the values of attributes. Comments and CDATA sections are written as they are. The attributes of an element are written in the order of its attribute list.

A document that holds nothing gives the declaration alone, or an empty string without one.

Note

An empty indent with prettyPrint True raises error 5, Invalid procedure call or argument, as soon as the document has an element.

Dim x As New cXml
x.CreateNew "r"
x.SetAttribute "r", "id", "1"
x.AddElement "r", "a"
x.SetElementText "r/a", "x < y"
x.AddElement "r", "empty"

Debug.Print x.ToXmlString()
Debug.Print x.ToXmlString(False)
' Output:
' <?xml version="1.0" encoding="UTF-8"?><r id="1"><a>x &lt; y</a><empty/></r>
' <r id="1"><a>x &lt; y</a><empty/></r>

Validate

Checks that every element and attribute has a name.

Syntax: object.Validate ( ) As Boolean

object
required An object expression that evaluates to a cXml.

Returns True when the object holds a document and every element in it has a name that is not empty and does not begin with /, and every attribute has a name that is not empty. Returns False otherwise, and for an object that holds no document.

That is all it checks. It does not test that the text was well formed, that the names are legal XML names, or anything about a schema or a DTD. In practice it detects an element added with an empty name, an attribute set with an empty name, and the element named /a that the parser makes from the self-closing tag <a/> (see Self-closing elements).

Dim x As New cXml
Debug.Print x.Validate()

x.Parse "<r><a></a></r>"
Debug.Print x.Validate()

x.Parse "<r><a/></r>"
Debug.Print x.Validate()

x.Parse "<a><b></a>"
Debug.Print x.Validate()
' Output:
' False
' True
' False
' True

Defects in version 1.0.0.0

Each of these sections describes a case in which the package gives a wrong result or never returns. The samples show the result of the version named here.

Self-closing elements

Dim x As New cXml
x.Parse "<r><a/><b x=""1""/><c>t</c></r>"

Debug.Print x.ToXmlString(False)
Debug.Print "[" & x.GetElementText("r/c") & "]"
Debug.Print x.GetElementText("r/b/c")
Debug.Print x.Validate()

x.CreateNew "r"
x.AddElement "r", "empty"
Dim y As New cXml
y.Parse x.ToXmlString(False)
Debug.Print y.RootElement.ChildNodes(1).Name
' Output:
' <r></a/><b x="1"><c>t</c></b></r>
' []
' t
' False
' /empty

Entities and character references

Dim x As New cXml
x.Parse "<a title=""Tom &amp; Jerry &#65;"">Tom &amp; Jerry</a>"

Debug.Print x.GetAttribute("a", "title")
Debug.Print x.GetElementText("a")
Debug.Print x.ToXmlString(False)
' Output:
' Tom & Jerry &#65;
' Tom &amp; Jerry
' <a title="Tom &amp; Jerry &amp;#65;">Tom &amp;amp; Jerry</a>

Text set with SetElementText or SetAttribute is plain text and is written correctly, once. A program that reads text with entities in it has to decode them itself.

Tags

Dim x As New cXml
x.Parse "<a b='1' c=""2""></a>"
Debug.Print x.GetAttribute("a", "b") & "|" & x.GetAttribute("a", "c")

x.Parse "<a b=""x>y""></a>"
Debug.Print x.GetAttribute("a", "b") & "|" & x.GetElementText("a")

x.Parse "<a b=""1""" & vbCrLf & "   c=""2""></a>"
Debug.Print x.GetAttribute("a", "b") & "|" & x.GetAttribute("a", "c") & "|" & x.RootElement.Attributes.Count

x.Parse "<a" & vbLf & "b=""1""></a>"
Debug.Print Replace(x.RootElement.Name, vbLf, "\n")

x.Parse "<a disabled c=""2""></a>"
Debug.Print x.GetAttribute("a", "disabled") & "|" & x.GetAttribute("a", "c")
' Output:
' 2|
' |y">
' 1||4
' a\nb="1"
' 2|

A tab between the attributes is read correctly, and so is a space around the =.

Comments, CDATA sections and declarations

These calls return, and show how a processing instruction and a DOCTYPE declaration are read:

Dim x As New cXml
x.Parse "<root><?pi data?><a>x</a></root>"
Debug.Print "[" & x.GetElementText("root") & "]"

x.Parse "<!DOCTYPE note SYSTEM ""Note.dtd""><note>text</note>"
Debug.Print "[" & x.GetElementText("note") & "]"

Dim y As New cXml
y.Parse "<?xml version=""1.0""?><!DOCTYPE note><note>text</note><?pi?>"
Debug.Print (y.RootElement Is Nothing)
' Output:
' [>x]
' [text]
' True

Each of these calls never returns. The sample is compiled and not run:

Dim x As New cXml
x.Parse "<root><!-- never closed </root>"
x.Parse "<root><![CDATA[ never closed </root>"
x.Parse "<?xml version=""1.0""?><!DOCTYPE root><root></root>"
x.Parse "<root><!DOCTYPE x><a>1</a></root>"

Malformed text and failed parses

Dim x As New cXml
x.Parse "<a>x</a>"

Debug.Print x.Parse("<")
Debug.Print x.Initialized & " " & x.Document.ChildNodes.Count
Debug.Print "[" & x.GetElementText("a") & "]"
' Output:
' Parse failed: Invalid procedure call or argument
' False
' True 0
' []

Pretty printing

Dim x As New cXml
x.CreateNew "r"
x.AddElement "r", "a"
x.SetElementText "r/a", "t"
x.AddElement "r", "empty"

Dim s As String
s = x.ToXmlString(False, True)
Debug.Print Replace(Replace(s, " ", "."), vbCrLf, "|")

Dim y As New cXml
y.Parse s
Debug.Print Replace(Replace(y.GetElementText("r/a"), " ", "."), vbCrLf, "|")
' Output:
' <r>|.<a>|..t|.</a>|.<empty/>|</r>|
' |..t|.

Clone, Merge and the root element

Dim x As New cXml
x.Parse "<r><a>1</a></r>"

Dim y As cXml
Set y = x.Clone()
Debug.Print y.RemoveElement("r/a")

Debug.Print x.RemoveElement("r")
Debug.Print x.RootElement.Name
Debug.Print "[" & x.ToXmlString(False) & "]"

x.Parse "just text"
Debug.Print x.RootElement.Name

Dim c As New cXml
c.Merge y
Debug.Print c.Initialized & " " & (c.RootElement Is Nothing)
' Output:
' False
' True
' r
' []
' r
' True True

A byte order mark in a string

Dim x As New cXml
x.Parse ChrW(&HFEFF) & "<root><a>t</a></root>"
Debug.Print x.RootElement.Name
' Output:
' a

Files

Dim path As String
path = Environ$("TEMP") & "\tbxml_files.xml"
If Dir(path) <> "" Then Kill path

Dim x As New cXml
Debug.Print x.LoadFromFile(path)
Debug.Print (Dir(path) <> "") & " " & FileLen(path)
Kill path

x.CreateNew "r"
x.Encoding = "ISO-8859-1"
x.AddElement "r", "t"
x.SetElementText "r/t", ChrW(233)
x.SaveToFile path

Dim y As New cXml
y.LoadFromFile path
Debug.Print y.Encoding & " " & AscW(y.GetElementText("r/t"))
Kill path

Dim f As Long
f = FreeFile
Open path For Output As #f
Print #f, "<r"
Close #f
Debug.Print y.LoadFromFile(path)
Kill path
' Output:
' LoadFromFile failed: Unspecified error
' False
' True 0
' ISO-8859-1 233
' Parse failed: Invalid procedure call or argument
' True

A file with fewer than three bytes cannot be loaded, and an empty file gives False: the code reads three bytes before it tests the length.

See Also

  • cXmlNode class – the nodes of the document
  • mXml module – the node types, the error numbers and the UTF-8 functions
  • TBMANLIB_Xml package – overview