Modificar dados XML usando XPathNavigator
A classe XPathNavigator fornece um conjunto de métodos usados para modificar nós e valores em um documento XML. Para usar esses métodos, o objeto XPathNavigator deve ser editável, ou seja, sua propriedade CanEdit deve ser true
.
Os objetos XPathNavigator que podem editar um documento XML são criados pelo método CreateNavigator da classe XmlDocument. Os objetos XPathNavigator criados pela classe XPathDocument são somente leitura e qualquer tentativa de usar os métodos de um objeto XPathNavigator criado por um objeto XPathDocument resultará em NotSupportedException.
Para saber mais sobre como criar objetos XPathNavigator editáveis, confira Leitura de dados XML usando XPathDocument e XmlDocument.
Modificando nós
Uma técnica simples para alterar o valor de um nó é usar os métodos SetValue e SetTypedValue da classe XPathNavigator.
A tabela a seguir lista os efeitos desses métodos em tipos de nós diferentes.
XPathNodeType | Dados alterados |
---|---|
Root | Não há suporte. |
Element | O conteúdo do elemento. |
Attribute | O valor do atributo. |
Text | O conteúdo do texto. |
ProcessingInstruction | O conteúdo, excluindo o destino. |
Comment | O conteúdo do comentário. |
Namespace | Sem suporte. |
A classe XPathNavigator também fornece um conjunto de métodos usados para inserir e remover nós. Para saber mais sobre como inserir e remover nós de um documento XML, consulte os tópicos Inserir dados XML usando XPathNavigator e Remover dados XML usando XPathNavigator.
Modificando valores sem tipo
O método SetValue simplesmente insere o valor sem tipo de string
passado como um parâmetro como o valor do nó no qual o objeto XPathNavigator está posicionado no momento. O valor é inserido sem nenhum tipo ou sem verificar se o novo valor é válido de acordo com o tipo de nó se as informações do esquema estiverem disponíveis.
No exemplo a seguir, o método SetValue é usado para atualizar todos os elementos price
no arquivo 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)
O exemplo usa o arquivo contosoBooks.xml
como entrada.
<?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>
Modificando valores com tipo
Quando o tipo de um nó é um tipo simples de Esquema XML do W3C, o novo valor inserido pelo método SetTypedValue é verificado em relação às facetas do tipo simples antes que o valor seja definido. Se o novo valor não for válido de acordo com o tipo de nó (por exemplo, definir um valor de -1
em um elemento cujo tipo seja xs:positiveInteger
), isso resultará em uma exceção.
O exemplo a seguir tenta alterar o valor do elemento price
do primeiro elemento book
no arquivo contosoBooks.xml
para um valor DateTime. Como o tipo de esquema XML do elemento price
está definido como xs:decimal
nos arquivos contosoBooks.xsd
, isso resulta em uma exceção.
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);
O exemplo usa o arquivo contosoBooks.xml
como entrada.
<?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>
O exemplo também usa contosoBooks.xsd
como entrada.
<?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>
Os efeitos de editar dados XML fortemente tipados
A classe XPathNavigator usa o Esquema XML do W3C como base para a descrição de XML fortemente tipado. Elementos e atributos podem ser anotados com informações de tipo com base na validação em relação a um documento de Esquema XML do W3C. Os elementos que podem conter outros elementos ou atributos são chamados de tipos complexos, enquanto os que podem conter apenas texto são chamados de tipos simples.
Observação
Os atributos podem ter apenas tipos simples.
Um elemento ou atributo poderá ser considerado como válido para esquema se estiver de acordo com as regras específicas para sua definição de tipo. Um elemento que tem o tipo simples xs:int
precisa conter um valor numérico entre -2147483648 e 2147483647 para ser válido para esquema. Para tipos complexos, a validade do esquema do elemento dependente da validade do esquema de seus elementos filho e atributos. Sendo assim, se um elemento é válido em relação a sua definição de tipo complexo, todos os seus elementos filho e atributos serão válidos com suas definições de tipo. Da mesma forma, mesmo que um dos elementos filho ou atributos de um elemento não seja válido em relação a sua definição de tipo, ou se tiver uma validade desconhecida, o elemento também será inválido ou de validade desconhecida.
Considerando que a validade de um elemento depende da validade de seus elementos filho e atributos, as alterações em algum deles resultará em alteração da validade do elemento se tiver sido válido anteriormente. Especificamente, se os elementos filho ou atributos de um elemento forem inseridos, atualizados ou excluídos, a validade do elemento se tornará desconhecida. Isso é representado pela propriedade Validity da propriedade SchemaInfo do elemento que está sendo definida como NotKnown. Além disso, este efeito propaga-se para cima recursivamente no documento XML, porque a validade do elemento pai do elemento (e seu elemento pai, e assim por diante) também se tornará desconhecida.
Para saber mais sobre validação do esquema e sobre a classe XPathNavigator, consulte Validação de esquema usando XPathNavigator.
Modificando atributos
Os métodos SetValue e SetTypedValue podem ser usados para modificar nós de atributo sem tipo e tipados bem como outros tipos de nós listados na seção “Modificando nós”.
O exemplo a seguir altera o valor do atributo genre
do primeiro elemento book
no arquivo 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);
Para obter mais informações sobre os métodos SetValue e SetTypedValue, consulte as seções "Modificando valores sem tipo" e "Modificando valores com tipo"
Propriedades InnerXml e OuterXml
As propriedades InnerXml e OuterXml da classe XPathNavigator alteram a marcação XML dos nós nos quais um objeto XPathNavigator está posicionado no momento.
A propriedade InnerXml altera a marcação XML dos nós filho no qual um objeto XPathNavigator está posicionado no momento com o conteúdo analisado da string
do XML determinada. Da mesma maneira, a propriedade OuterXml altera a marcação XML dos nós filho no qual um objeto XPathNavigator está posicionado no momento além do próprio nó atual.
O exemplo a seguir usa a propriedade OuterXml para modificar o valor do elemento price
e inserir um novo atributo discount
no primeiro elemento book
no arquivo 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);
O exemplo usa o arquivo contosoBooks.xml
como entrada.
<?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>
Modificando nós de namespace
No DOM (Document Object Model), as declarações de namespace são tratadas como se fossem atributos normais que podem ser inseridos, atualizados e excluídos. A classe XPathNavigator não permite essas operações em nós de namespace porque alterar o valor de um nó de namespace pode modificar a identidade dos elementos e dos atributos dentro do escopo do nó do namespace conforme ilustrado no exemplo a seguir.
<root xmlns="http://www.contoso.com">
<child />
</root>
Se o exemplo de XML anterior for modificado da seguinte forma, isso efetivamente renomeará cada elemento no documento porque o valor da URI do namespace de cada elemento será alterado.
<root xmlns="urn:contoso.com">
<child />
</root>
Inserir os nós de namespace que não entrem em conflito com as declarações de namespace no escopo em que estão inseridos dentro é permitido pela classe XPathNavigator. Nesse caso, as declarações de namespace não são declaradas em escopos menores no documento XML e não resultam em renomeação conforme ilustrado no exemplo a seguir.
<root xmlns:a="http://www.contoso.com">
<parent>
<a:child />
</parent>
</root>
Se o exemplo de XML acima for modificado da seguinte forma, as declarações de namespace serão propagadas corretamente no documento XML abaixo do escopo de outra declaração de namespace.
<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>
No exemplo de XML acima, o atributo a:parent-id
é inserido no elemento parent
no namespace http://www.contoso.com/parent-id
. O método CreateAttribute é usado para inserir o atributo enquanto estiver posicionado no elemento parent
. A declaração do namespace http://www.contoso.com
é inserida automaticamente pela classe XPathNavigator para manter a consistência do restante do documento XML.
Modificando nós de referência da entidade
Os nós de referência de entidade em um objeto XmlDocument são somente leitura e não podem ser editados usando as classes XPathNavigator ou XmlNode. Qualquer tentativa de modificar um nó de referência de entidade resulta em um InvalidOperationException.
Modificando nós xsi:nil
A recomendação do Esquema XML do W3C apresenta o conceito de um elemento ser nillable. Quando um elemento é nillable, é possível que ele não tenha nenhum conteúdo e ainda ser válido. O conceito de um elemento ser nillable é semelhante ao conceito de um objeto ser null
. A principal diferença é que um objeto null
não pode ser acessado de nenhuma forma, enquanto que um elemento xsi:nil
ainda tem propriedades como atributos que podem ser acessados, mas não tem conteúdo (elementos filho ou texto). A existência do atributo xsi:nil
com um valor true
em um elemento em um documento XML é usada para indicar que um elemento não tem conteúdo.
Se um objeto XPathNavigator for usado para adicionar conteúdo a um elemento válido com um atributo xsi:nil
com um valor true
, o valor do seu atributo xsi:nil
será definido como false
.
Observação
Se o conteúdo de um elemento com um atributo xsi:nil
definido como false
for excluído, o valor do atributo não será alterado para true
.
Salvando um documento XML
Salvar as alterações feitas em um objeto XmlDocument como resultado dos métodos de edição descritos neste tópico é realizado usando os métodos da classe XmlDocument. Para saber mais sobre como salvar as alterações feitas em um objeto XmlDocument, confira Salvar e gravar um documento.