Изменение XML-данных с помощью класса XPathNavigator

Класс XPathNavigator предоставляет набор методов для изменения узлов и значений в XML-документе. Для использования этих методов необходимо сделать редактируемым объект XPathNavigator, то есть установить для свойства CanEdit значение true.

Объекты XPathNavigator для правки XML-документа создаются с помощью метода CreateNavigator класса XmlDocument. Объекты XPathNavigator, созданные классом XPathDocument, доступны только для чтения, и любая попытка вызова методов редактирования объекта XPathNavigator, созданного объектом XPathDocument, приводит к возникновению исключения NotSupportedException.

Дополнительные сведения о доступных только для чтения и изменяемых объектах XPathNavigator см. в руководстве по чтению данных XML с помощью XPathDocument и XmlDocument.

Изменение узлов

Удобнее всего изменять значение узла с помощью методов SetValue и SetTypedValue класса XPathNavigator.

В следующей таблице описывается действие этих методов на различные типы узлов.

XPathNodeType Измененные данные
Root Не поддерживается.
Element Содержимое элемента.
Attribute Значение атрибута.
Text Текстовое содержимое.
ProcessingInstruction Содержимое, за исключением цели.
Comment Содержимое комментария.
Namespace Не поддерживается

Примечание.

Изменение узлов Namespace и узла Root не поддерживается.

Класс XPathNavigator предоставляет также набор методов для вставки и удаления узлов. Дополнительные сведения о вставке и удалении узлов в XML-документах вы найдете в статьях Вставка XML-данных с помощью XPathNavigator и Удаление XML-данных с помощью XPathNavigator.

Изменение нетипизированных значений

Метод SetValue просто вставляет нетипизированное значение string, переданное в качестве параметра, как значение узла, на котором в данный момент позиционируется объект XPathNavigator. Значение вставляется без какого-либо типа и без проверки допустимости нового значения в соответствии с типом узла, если доступны сведения о схеме.

В следующем примере метод SetValue используется для обновления всех элементов price в файле contosoBooks.xml.

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)

В примере в качестве входных данных используется файл contosoBooks.xml.

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

Изменение типизированных значений

Когда тип узла является простым типом XML-схемы W3C, новое значение, вставленное методом SetTypedValue, проверяется по особенностям простого типа, прежде чем будет установлено значение. Если новое значение недопустимо в соответствии с типом узла (например, при установке значения -1 для элемента с типом xs:positiveInteger), возникает исключение.

В следующем примере предпринимается попытка изменить значение элемента price первого элемента book в файле contosoBooks.xml на DateTime. Так как в файлах price тип элемента xs:decimal по XML-схеме определен как contosoBooks.xsd, возникает исключение.

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);  

В примере в качестве входных данных используется файл contosoBooks.xml.

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

В примере также в качестве входных данных используется contosoBooks.xsd.

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

Последствия изменения строго типизированных XML-данных

Класс XPathNavigator использует схему XML W3C как основу для описания строго типизированного XML. Элементы и атрибуты могут сопровождаться информацией о типе, создаваемой по результатам проверки на основе документа схемы XML W3C. Элементы, содержащие другие элементы или атрибуты, называются сложными типами, а имеющие только текстовое содержимое - простыми типами.

Примечание.

Атрибуты могут иметь только простой тип.

Элемент или атрибут считается соответствующим схеме, если он соответствует всем правилам, специфичным для определения его типа. Элемент, относящийся к простому типу xs:int, чтобы соответствовать схеме, должен содержать числовое значение в диапазоне от -2147483648 до 2147483647. Для сложных типов соответствие элемента схеме определяется на основе соответствия схеме его дочерних элементов и атрибутов. Таким образом, если элемент соответствует определению своего сложного типа, все его дочерние элементы и атрибуты соответствуют определениям своих типов. Аналогично, если хотя бы один из дочерних элементов или атрибутов элемента не соответствует определению своего типа или факт его соответствия не установлен, весь элемент также не соответствует или его соответствие не установлено.

Поскольку соответствие элемента зависит от соответствия его дочерних элементов и атрибутов, их изменения могут повлиять на соответствие элемента, который ранее соответствовал. Конкретнее, если дочерние элементы или атрибуты элемента добавляются, изменяются или удаляются, элемент переходит в состояние неустановленного соответствия. Чтобы отразить этот факт, свойство Validity свойства SchemaInfo элемента получает значение NotKnown. Затем этот эффект рекурсивно распространяется вверх по XML-документу, поскольку соответствие родительского элемента данного элемента (и его родительского элемента и т. п.) также приходит в неустановленное состояние.

Дополнительные сведения о проверке схемы и классе XPathNavigator см. в статье Проверка по схеме с помощью XPathNavigator.

Изменение атрибутов

Для изменения узлов типизированных и нетипизированных атрибутов, а также других типов узлов, перечисленных в разделе «Изменение узлов», применяются методы SetValue и SetTypedValue.

В следующем примере изменяется значение атрибута genre первого элемента book в файле books.xml.

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);  

Дополнительные сведения о методах SetValue и SetTypedValue см. в разделах «Изменение нетипизированных значений» и «Изменение типизированных значений».

Свойства InnerXml и OuterXml

Свойства InnerXml и OuterXml класса XPathNavigator изменяют XML-разметку узлов, на которых в данный момент позиционируется объект XPathNavigator.

Свойство InnerXml изменяет XML-разметку дочерних узлов, на которых в данный момент позиционируется объект XPathNavigator, разобранный содержимым заданной XML-строки (string). Подобным образом свойство OuterXml изменяет XML-разметку дочерних узлов, на которых в данный момент позиционируется объект XPathNavigator, так же как и самого текущего узла.

В следующем примере свойство OuterXml используется для изменения значения элемента price и вставки нового атрибута discount в первый элемент book в файле contosoBooks.xml.

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);  

В примере в качестве входных данных используется файл contosoBooks.xml.

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

Изменение узлов пространств имен

В модели DOM декларации пространств имен обрабатываются как обычные атрибуты, которые можно вставлять, изменять и удалять. Класс XPathNavigator не допускает подобных операций на узлах пространств имен, поскольку изменение значения узла пространства имен может изменить идентификатор элементов и атрибутов в области действия узла пространства имен, как показано в следующем примере.

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

Если изменить приведенный выше пример следующим образом, это эквивалентно переименованию всех элементов документа, поскольку изменится значение URI-кода пространства имен всех элементов.

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

Класс XPathNavigator разрешает вставлять узлы пространств имен, не конфликтующие с декларациями пространств имен в данной области действия. В данном случае декларации пространств имен не производятся в нижележащих областях действия XML-документа и не вызывают переименования, как показано в следующем примере.

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

Если изменить приведенный выше пример следующим образом, декларации пространств имен корректно распространяются по всему XML-документу ниже областей действия других деклараций пространств имен.

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

В приведенном выше примере атрибут a:parent-id вставляется в элемент parent в пространстве имен http://www.contoso.com/parent-id. Метод CreateAttribute используется для вставки атрибута для текущего элемента parent. Декларация пространства имен http://www.contoso.com автоматически вставляется классом XPathNavigator для сохранения согласованности всего остального XML-документа.

Изменение узлов ссылок на сущности

Ссылки на сущности в объекте XmlDocument служат только для чтения; их нельзя изменять ни с помощью класса XPathNavigator, ни с помощью класса XmlNode. Любые попытки изменения узла ссылки на сущность приводят к исключению InvalidOperationException.

Изменение узлов xsi:nil

Рекомендации схемы XML W3C содержат понятие обнуляемого (nillable) элемента. Обнуляемый элемент может не иметь никакого содержимого и все же быть допустимым. Понятие обнуляемого элемента аналогично понятию объекта со значением null. Основное различие заключается в том, что объект со значением null недоступен ни для каких взаимодействий, в то время как элемент xsi:nil все же обладает доступными свойствами (например, атрибутами), но не имеет содержимого (дочерних элементов или текста). Существование атрибута xsi:nil со значением true у элемента в XML-документе используется для обозначения отсутствия содержимого у данного элемента.

Если объект XPathNavigator используется для добавления содержимого к допустимому элементу с атрибутом xsi:nil со значением true, для его атрибута xsi:nil будет установлено значение false.

Примечание.

Если содержимое элемента с атрибутом xsi:nil, значение которого равно false, удалить, значение атрибута не будет изменено на true.

Сохранение XML-документа

Сохранение изменений, внесенных в объект XmlDocument с помощью описанных в данном разделе методов, выполняется с помощью методов класса XmlDocument. Дополнительные сведения о сохранении изменений, внесенных в объект XmlDocument, см. в руководстве по созданию и сохранению документов.

См. также