Modify XML Data using XPathNavigator

The XPathNavigator class provides a set of methods used to modify nodes and values in an XML document. In order to use these methods, the XPathNavigator object must be editable, that is, its CanEdit property must be true.

XPathNavigator objects that can edit an XML document are created by the CreateNavigator method of the XmlDocument class. XPathNavigator objects created by the XPathDocument class are read-only and any attempt to use the editing methods of an XPathNavigator object created by an XPathDocument object result in a NotSupportedException.

For more information about creating editable XPathNavigator objects, see Reading XML Data using XPathDocument and XmlDocument.

Modifying Nodes

A simple technique for changing the value of a node is to use the SetValue and SetTypedValue methods of the XPathNavigator class.

The following table lists the effects of these methods on different node types.

XPathNodeType Data Changed
Root Not supported.
Element The content of the element.
Attribute The value of the attribute.
Text The text content.
ProcessingInstruction The content, excluding the target.
Comment The content of the comment.
Namespace Not Supported.

Note

Editing Namespace nodes or the Root node is not supported.

The XPathNavigator class also provides a set of methods used to insert and remove nodes. For more information about inserting and removing nodes from an XML document, see the Insert XML Data using XPathNavigator and Remove XML Data using XPathNavigator topics.

Modifying Untyped Values

The SetValue method simply inserts the untyped string value passed as a parameter as the value of the node the XPathNavigator object is currently positioned on. The value is inserted without any type or without verifying that the new value is valid according to the type of the node if schema information is available.

In the following example, the SetValue method is used to update all price elements in the contosoBooks.xml file.

XmlDocument^ document = gcnew XmlDocument();
document->Load("contosoBooks.xml");
XPathNavigator^ navigator = document->CreateNavigator();

XmlNamespaceManager^ manager = gcnew XmlNamespaceManager(navigator->NameTable);
manager->AddNamespace("bk", "http://www.contoso.com/books");

for each (XPathNavigator^ nav in navigator->Select("//bk:price", manager))
{
    if(nav->Value == "11.99")
    {
        nav->SetValue("12.99");
    }
}

Console::WriteLine(navigator->OuterXml);
XmlDocument document = new XmlDocument();
document.Load("contosoBooks.xml");
XPathNavigator navigator = document.CreateNavigator();

XmlNamespaceManager manager = new XmlNamespaceManager(navigator.NameTable);
manager.AddNamespace("bk", "http://www.contoso.com/books");

foreach (XPathNavigator nav in navigator.Select("//bk:price", manager))
{
    if (nav.Value == "11.99")
    {
        nav.SetValue("12.99");
    }
}

Console.WriteLine(navigator.OuterXml);
Dim document As XmlDocument = New XmlDocument()
document.Load("contosoBooks.xml")
Dim navigator As XPathNavigator = document.CreateNavigator()

Dim manager As XmlNamespaceManager = New XmlNamespaceManager(navigator.NameTable)
manager.AddNamespace("bk", "http://www.contoso.com/books")

For Each nav As XPathNavigator In navigator.Select("//bk:price", manager)
    If nav.Value = "11.99" Then
        nav.SetValue("12.99")
    End If
Next

Console.WriteLine(navigator.OuterXml)

The example takes the contosoBooks.xml file as an input.

<?xml version="1.0" encoding="utf-8" ?>
<bookstore xmlns="http://www.contoso.com/books">
    <book genre="autobiography" publicationdate="1981-03-22" ISBN="1-861003-11-0">
        <title>The Autobiography of Benjamin Franklin</title>
        <author>
            <first-name>Benjamin</first-name>
            <last-name>Franklin</last-name>
        </author>
        <price>8.99</price>
    </book>
    <book genre="novel" publicationdate="1967-11-17" ISBN="0-201-63361-2">
        <title>The Confidence Man</title>
        <author>
            <first-name>Herman</first-name>
            <last-name>Melville</last-name>
        </author>
        <price>11.99</price>
    </book>
    <book genre="philosophy" publicationdate="1991-02-15" ISBN="1-861001-57-6">
        <title>The Gorgias</title>
        <author>
            <name>Plato</name>
        </author>
        <price>9.99</price>
    </book>
</bookstore>

Modifying Typed Values

When the type of a node is a W3C XML Schema simple type, the new value inserted by the SetTypedValue method is checked against the facets of the simple type before the value is set. If the new value is not valid according to the type of the node (for example, setting a value of -1 on an element whose type is xs:positiveInteger), it results in an exception.

The following example attempts to change the value of the price element of the first book element in the contosoBooks.xml file to a DateTime value. Because the XML Schema type of the price element is defined as xs:decimal in the contosoBooks.xsd files, this results in an exception.

Dim settings As XmlReaderSettings = New XmlReaderSettings()  
settings.Schemas.Add("http://www.contoso.com/books", "contosoBooks.xsd")  
settings.ValidationType = ValidationType.Schema  
  
Dim reader As XmlReader = XmlReader.Create("contosoBooks.xml", settings)  
  
Dim document As XmlDocument = New XmlDocument()  
document.Load(reader)  
Dim navigator As XPathNavigator = document.CreateNavigator()  
  
navigator.MoveToChild("bookstore", "http://www.contoso.com/books")  
navigator.MoveToChild("book", "http://www.contoso.com/books")  
navigator.MoveToChild("price", "http://www.contoso.com/books")  
  
navigator.SetTypedValue(DateTime.Now)  
XmlReaderSettings settings = new XmlReaderSettings();  
settings.Schemas.Add("http://www.contoso.com/books", "contosoBooks.xsd");  
settings.ValidationType = ValidationType.Schema;  
  
XmlReader reader = XmlReader.Create("contosoBooks.xml", settings);  
  
XmlDocument document = new XmlDocument();  
document.Load(reader);  
XPathNavigator navigator = document.CreateNavigator();  
  
navigator.MoveToChild("bookstore", "http://www.contoso.com/books");  
navigator.MoveToChild("book", "http://www.contoso.com/books");  
navigator.MoveToChild("price", "http://www.contoso.com/books");  
  
navigator.SetTypedValue(DateTime.Now);  

The example takes the contosoBooks.xml file as an input.

<?xml version="1.0" encoding="utf-8" ?>
<bookstore xmlns="http://www.contoso.com/books">
    <book genre="autobiography" publicationdate="1981-03-22" ISBN="1-861003-11-0">
        <title>The Autobiography of Benjamin Franklin</title>
        <author>
            <first-name>Benjamin</first-name>
            <last-name>Franklin</last-name>
        </author>
        <price>8.99</price>
    </book>
    <book genre="novel" publicationdate="1967-11-17" ISBN="0-201-63361-2">
        <title>The Confidence Man</title>
        <author>
            <first-name>Herman</first-name>
            <last-name>Melville</last-name>
        </author>
        <price>11.99</price>
    </book>
    <book genre="philosophy" publicationdate="1991-02-15" ISBN="1-861001-57-6">
        <title>The Gorgias</title>
        <author>
            <name>Plato</name>
        </author>
        <price>9.99</price>
    </book>
</bookstore>

The example also takes the contosoBooks.xsd as an input.

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.contoso.com/books" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="bookstore">
        <xs:complexType>
            <xs:sequence>
                <xs:element maxOccurs="unbounded" name="book">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="title" type="xs:string" />
                            <xs:element name="author">
                                <xs:complexType>
                                    <xs:sequence>
                                        <xs:element minOccurs="0" name="name" type="xs:string" />
                                        <xs:element minOccurs="0" name="first-name" type="xs:string" />
                                        <xs:element minOccurs="0" name="last-name" type="xs:string" />
                                    </xs:sequence>
                                </xs:complexType>
                            </xs:element>
                            <xs:element name="price" type="xs:decimal" />
                        </xs:sequence>
                        <xs:attribute name="genre" type="xs:string" use="required" />
                        <xs:attribute name="publicationdate" type="xs:date" use="required" />
                        <xs:attribute name="ISBN" type="xs:string" use="required" />
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

The Effects of Editing Strongly Typed XML Data

The XPathNavigator class uses the W3C XML Schema as a basis for describing strongly typed XML. Elements and attributes can be annotated with type information based on validation against a W3C XML Schema document. Elements that can contain other elements or attributes are called complex types, while those that can only contain textual content are called simple types.

Note

Attributes can only have simple types.

An element or attribute can be considered to be schema-valid if it conforms to all the rules specific to its type definition. An element that has the simple type xs:int has to contain a numeric value between -2147483648 and 2147483647 to be schema-valid. For complex types, the schema-validity of the element is dependent on the schema-validity of its child elements and attributes. Thus if an element is valid against its complex type definition, all its child elements and attributes are valid against their type definitions. Similarly, if even one of the child elements or attributes of an element is invalid against its type definition, or has an unknown validity, the element is also either invalid or of unknown validity.

Given that the validity of an element is dependent on the validity of its child elements and attributes, modifications to either result in altering the validity of the element if it was previously valid. Specifically, if the child elements or attributes of an element are inserted, updated, or deleted, then the validity of the element becomes unknown. This is represented by the Validity property of the element's SchemaInfo property being set to NotKnown. Furthermore, this effect cascades upwards recursively across the XML document, because the validity of the element's parent element (and its parent element, and so on) also becomes unknown.

For more information about schema validation and the XPathNavigator class, see Schema Validation using XPathNavigator.

Modifying Attributes

The SetValue and SetTypedValue methods can be used to modify untyped and typed attribute nodes as well as the other node types listed in the "Modifying Nodes" section.

The following example changes the value of the genre attribute of the first book element in the books.xml file.

Dim document As XmlDocument = New XmlDocument()  
document.Load("books.xml")  
Dim navigator As XPathNavigator = document.CreateNavigator()  
  
navigator.MoveToChild("bookstore", String.Empty)  
navigator.MoveToChild("book", String.Empty)  
navigator.MoveToAttribute("genre", String.Empty)  
  
navigator.SetValue("non-fiction")  
  
navigator.MoveToRoot()  
Console.WriteLine(navigator.OuterXml)  
XmlDocument document = new XmlDocument();  
document.Load("books.xml");  
XPathNavigator navigator = document.CreateNavigator();  
  
navigator.MoveToChild("bookstore", String.Empty);  
navigator.MoveToChild("book", String.Empty);  
navigator.MoveToAttribute("genre", String.Empty);  
  
navigator.SetValue("non-fiction");  
  
navigator.MoveToRoot();  
Console.WriteLine(navigator.OuterXml);  

For more information about the SetValue and SetTypedValue methods, see the "Modifying Untyped Values" and "Modifying Typed Values" sections.

InnerXml and OuterXml Properties

The InnerXml and OuterXml properties of the XPathNavigator class change the XML markup of the nodes an XPathNavigator object is currently positioned on.

The InnerXml property changes the XML markup of the child nodes an XPathNavigator object is currently positioned on with the parsed contents of the given XML string. Similarly, the OuterXml property changes the XML markup of the child nodes an XPathNavigator object is currently positioned on as well as the current node itself.

The following example uses the OuterXml property to modify the value of the price element and insert a new discount attribute on the first book element in the contosoBooks.xml file.

Dim document As XmlDocument = New XmlDocument()  
document.Load("contosoBooks.xml");  
Dim navigator As XPathNavigator = document.CreateNavigator()  
  
navigator.MoveToChild("bookstore", "http://www.contoso.com/books")  
navigator.MoveToChild("book", "http://www.contoso.com/books")  
navigator.MoveToChild("price", "http://www.contoso.com/books")  
  
navigator.OuterXml = "<price discount=\"0\">10.99</price>"  
  
navigator.MoveToRoot()  
Console.WriteLine(navigator.OuterXml)  
XmlDocument document = new XmlDocument();  
document.Load("contosoBooks.xml");  
XPathNavigator navigator = document.CreateNavigator();  
  
navigator.MoveToChild("bookstore", "http://www.contoso.com/books");  
navigator.MoveToChild("book", "http://www.contoso.com/books");  
navigator.MoveToChild("price", "http://www.contoso.com/books");  
  
navigator.OuterXml = "<price discount=\"0\">10.99</price>";  
  
navigator.MoveToRoot();  
Console.WriteLine(navigator.OuterXml);  

The example takes the contosoBooks.xml file as an input.

<?xml version="1.0" encoding="utf-8" ?>
<bookstore xmlns="http://www.contoso.com/books">
    <book genre="autobiography" publicationdate="1981-03-22" ISBN="1-861003-11-0">
        <title>The Autobiography of Benjamin Franklin</title>
        <author>
            <first-name>Benjamin</first-name>
            <last-name>Franklin</last-name>
        </author>
        <price>8.99</price>
    </book>
    <book genre="novel" publicationdate="1967-11-17" ISBN="0-201-63361-2">
        <title>The Confidence Man</title>
        <author>
            <first-name>Herman</first-name>
            <last-name>Melville</last-name>
        </author>
        <price>11.99</price>
    </book>
    <book genre="philosophy" publicationdate="1991-02-15" ISBN="1-861001-57-6">
        <title>The Gorgias</title>
        <author>
            <name>Plato</name>
        </author>
        <price>9.99</price>
    </book>
</bookstore>

Modifying Namespace Nodes

In the Document Object Model (DOM), namespace declarations are treated as if they are regular attributes that can be inserted, updated and deleted. The XPathNavigator class does not allow such operations on namespace nodes because altering the value of a namespace node can change the identity of the elements and attributes within the scope of the namespace node as illustrated in the following example.

<root xmlns="http://www.contoso.com">  
    <child />  
</root>  

If the XML example above is changed in the following way, this effectively renames every element in the document because the value of each element's namespace URI is changed.

<root xmlns="urn:contoso.com">  
    <child />  
</root>  

Inserting namespace nodes that do not conflict with namespace declarations at the scope that they are inserted in is allowed by the XPathNavigator class. In this case, the namespace declarations are not declared at lower scopes in the XML document and does not result in renaming as illustrated in the following example.

<root xmlns:a="http://www.contoso.com">  
    <parent>  
        <a:child />  
    </parent>  
</root>  

If the XML example above is changed in the following way, the namespace declarations are correctly propagated across the XML document below the scope of the other namespace declaration.

<root xmlns:a="http://www.contoso.com">  
    <parent a:parent-id="1234" xmlns:a="http://www.contoso.com/parent-id">  
        <a:child xmlns:a="http://www.contoso.com/" />  
    </parent>  
</root>  

In the XML example above, the attribute a:parent-id is inserted on the parent element in the http://www.contoso.com/parent-id namespace. The CreateAttribute method is used to insert the attribute while positioned on the parent element. The http://www.contoso.com namespace declaration is automatically inserted by the XPathNavigator class to preserve the consistency of the rest of the XML document.

Modifying Entity Reference Nodes

Entity reference nodes in an XmlDocument object are read-only and cannot be edited using either the XPathNavigator or XmlNode classes. Any attempt to modify an entity reference node results in an InvalidOperationException.

Modifying xsi:nil Nodes

The W3C XML Schema recommendation introduces the concept of an element being nillable. When an element is nillable, it is possible for the element to have no content and still be valid. The concept of an element being nillable is similar to the concept of an object being null. The main difference is that a null object cannot be accessed in any way, while an xsi:nil element still has properties such as attributes that can be accessed, but has no content (child elements or text). The existence of the xsi:nil attribute with a value of true on an element in an XML document is used to indicate that an element has no content.

If an XPathNavigator object is used to add content to a valid element with an xsi:nil attribute with a value of true, the value of its xsi:nil attribute is set to false.

Note

If the content of an element with an xsi:nil attribute set to false is deleted, the value of the attribute is not changed to true.

Saving an XML Document

Saving changes made to an XmlDocument object as the result of the editing methods described in this topic is performed using the methods of the XmlDocument class. For more information about saving changes made to an XmlDocument object, see Saving and Writing a Document.

See also