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. |
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.