- Acesso aos dados XML no PowerShell
- Acesso ao XML com XPath.
- Acesso a XML como Objectos.
- Comparação de XPath e Abordagens de Objetos
- Modificar ou Criar Dados XML
- Adicionar Dados XML
- Usar XML para Serialização de Objetos
Introdução
As com qualquer linguagem de alto nível, the data
está no coração do PowerShell. No caso do PowerShell, isto resume-se à conversão de dados externos em objectos PowerShell e vice versa. Este é o segundo de uma série de artigos que mostra como importar quase todos os formatos de dados comuns que você provavelmente encontrará, e como exportar para alguns deles também. O primeiro artigo da série, PowerShell Data Basics: Dados Baseados em Arquivos, cobre uma variedade de formatos de texto, desde arquivos de largura fixa, largura variável e ragged-right até CSV, listas de propriedades, arquivos INI e dados JSON, e conclui com um tratamento de importação e exportação para o Excel. Aqui nos concentramos em obter o máximo do XML.
Acessando dados XML no PowerShell
Existem duas técnicas embutidas para trabalhar com dados XML no PowerShell; a abordagem XPath e a abordagem object dot-notation. Vamos descrever e comparar essas duas abordagens e experimentá-las em alguns exemplos de XML. Por conveniência, todos os exemplos de código utilizam este arquivo XML de amostra do MSDN. A Figura 1 mostra uma representação do esquema subjacente ao arquivo em um formato conciso (particularmente quando comparado à leitura do arquivo XSD bruto!), cortesia do Visual Studio’s XML Schema Explorer. A partir da figura, você vai notar que o arquivo XML é um catálogo que contém uma coleção de livros. Cada livro tem sete características, seis das quais são elementos filhos e uma que é um atributo filho.
Figure 1 Schema for Microsoft’s sample XML fileo:p>
Para carregar este arquivo XML de amostra, você pode usar qualquer um destes:
se você preferir experimentar com dados XML imediatos em vez de carregar um arquivo XML em um XmlDocument, é simples de fazer. Simplesmente defina o seu XML como uma string e coloque-o no tipo XML, como acabamos de fazer com o cmdlet Get-Content para arquivos. Aqui está uma parte do arquivo XML de exemplo com apenas dois livros:
Acessando XML com XPath
Com o arquivo carregado em um objeto XmlDocument, você pode então navegar na árvore XML com XPath. Para selecionar um conjunto de nós use o método SelectNodes:
$xdoc.SelectNodes(“//autor”)
#texto
>
–972>>
Gambardella, Matthew
>
Ralls, Kim
>
Corets, Eva
>
Corets, Eva
>
Corets, Eva
>
Randall, Cynthia
Thurman, Paula
Knorr, Stefan
Kress, Peter
O’Brien, Tim
O’Brien, Tim
Galos, Mike
Ou use SelectSingleNode para retornar apenas um nó:
$xdoc.SelectSingleNode(“//book”)
id : bk102
>
author : Ralls, Kim
>
title : Midnight Rain
genre : Fantasia
>
preço : 5.95
data de publicação : 2000-12-16
descrição : Uma ex-arquiteta luta contra zumbis corporativos,
>
uma feiticeira malvada, e sua própria infância para se tornar rainha
>
do mundo.
>
Notificação no primeiro exemplo de que existem duplicatas. Suponha, em vez disso, que você queira uma lista de autores únicos no catálogo. Você pode pensar que algo como …
1
|
$xdoc.SelectNodes(“//autor”) | select -Unique
|
…funcionaria, mas na verdade retorna apenas o nome do primeiro autor. Para entender porque isso falhou, você precisaria entender mais sobre a estrutura dos documentos XML. O primeiro exemplo realmente retornou uma lista de nós de texto (ou seja, não uma lista de texto) e o filtro único está operando nessa lista, procurando pelo tipo de elemento único. Como todos os itens da coleção são nós de texto, e todos são do mesmo tipo de elemento, todos os nós além do primeiro são, portanto, considerados duplicados. O resultado é que apenas o primeiro é retornado.
O que você realmente quer é o valor da string de cada nó de autor. A partir do nó autor você deve primeiro acessar seu nó de texto (seu primeiro e único filho), depois o valor desse nó de texto (linha A no próximo exemplo). Alternativamente, você pode usar uma expressão um pouco mais curta com a propriedade InnerText (linha B). Mais uma variação utiliza o Select-Xml cmdlet que evita uma chamada de método e assim é, em certo sentido, uma abordagem mais distinta do PowerShell (linha C). Todas as três linhas retornam o mesmo resultado.
SelectNodes e SelectSingleNode juntos dão-lhe uma funcionalidade equivalente a Select-Xml. Ambos suportam namespaces, que eu ainda não mencionei. Os dois métodos ambos tomam um XmlNamespaceManager como um segundo parâmetro opcional, enquanto o Select-Xml cmdlet toma um parâmetro opcional Namespace que especifica seus namespaces em uma tabela hash.
SelectSingleNode retorna um XmlNode e SelectNodes retorna um XmlNodeList. Select-Xml, por outro lado, retorna um objeto SelectXmlInfo (ou um array deles) e sua propriedade Node fornece acesso ao nó subjacente. O exemplo acima ilustra estas diferenças.
Acessando XML como Objetos
Com o mesmo objeto XmlDocument da última seção, PowerShell também fornece suporte a objetos dinâmicos para dados XML: Isto permite acessar dados XML como objetos PowerShell de primeira classe, não requerendo nem seletor XPath nem familiaridade com os detalhes de coisas como nós de XML, valores ou nós de texto. Além disso, você obtém Intellisense instantâneo e dinâmico do seu esquema XML quando carrega seus dados XML! A Figura 2 ilustra isso para o PowerShell ISE, onde você obtém escolhas de seleção e preenchimento de palavras, assim como com os tokens nativos do PowerShell. Note particularmente na expansão inferior que ele procura pelo que você digitou em qualquer lugar no nome da propriedade, e não apenas a partir do primeiro caractere: Intellisense encontraria uma propriedade de data aqui, seja ela chamada de published_date ou releaseDate ou date_of_publication. Note que você tem o completamento de palavras disponível no PowerShell V2 ou V3, e no PowerShell ISE ou no console PowerShell. Mas as escolhas de seleção só estão disponíveis no PowerShell ISE em V3.
>
Figure 2 Automatic Intellisense ao carregar um documento XML
>
Aqui estão alguns exemplos para mostrar que o XML é de fato auto-convertido para objetos PowerShell:
$xdoc
>
xml catalog
– —
>
version=”1.0″ catálogo
>
>
$xdoc.catalog
>
book
>
—
>
{book, book, book, book…}
$xdoc.catalog.book | Format-Table -AutoSize
id author title genre price publish_date description
– — — — — — —- —-
bk101 Gambardella, Matthew XML Developer’s Guide Computer 44.95 2000-10-01 Um olhar em profundidade …
bk102 Ralls, Kim Midnight Rain Fantasy 5.95 2000-12-16 A former architec…
bk103 Corets, Eva Maeve Ascendant Fantasy 5.95 2000-11-17 After the colaps…
bk104 Corets, Eva Oberon’s Legacy Fantasy 5.95 2001-03-10 Em pós-apocalyps…
bk105 Corets, Eva The Sundered Grail Fantasy 5.95 2001-09-10 As duas filhas…
bk106 Randall, Cynthia Lover Birds Romance 4.95 2000-09-02 Quando Carla se encontra …
bk107 Thurman, Paula Splish Splash Romance 4.95 2000-11-02 Um mergulhador de águas profundas …
bk108 Knorr, Stefan Creepy Crawlies Horror 4.95 2000-12-06 Uma antologia de h…
bk109 Kress, Peter Paradox Lost Science Fiction 6.95 2000-11-02 Após um inadvertido…
bk110 O’Brien, Tim Microsoft .NET: The Prog… Computer 36.95 2000-12-09 Microsoft .NET …
bk111 O’Brien, Tim MSXML3: A Comprehensive … Computer 36.95 2000-12-01 The Microsoft MSX…
bk112 Galos, Mike Visual Studio 7: A Comprh… Computer 49.95 2001-04-16 Microsoft Visual …
$xdoc.catalog.book
id : bk103
>
author : Corets, Eva
>
title : Maeve Ascendant
>
genre : Fantasia
>
preço : 5.95
>
data de publicação: 2000-11-17
descrição: Após o colapso de uma nanotecnologia
sociedade na Inglaterra, os jovens sobreviventes lançam a
>
fundação para uma nova sociedade
>
$xdoc.catalog.book.author
>
Randall, Cynthia
>
$xdoc.catalog.book.id
bk106
Note que todos os nós XML no documento são convertidos em propriedades PowerShell padrão, se um nó tem filhos (por exemplo, catálogo) ou é um nó de folha (por exemplo, preço), ou se um nó de folha é um elemento (por exemplo, autor) ou um atributo (por exemplo, id). Em particular (como os dois últimos exemplos acima ilustram), valores de elementos e valores de atributos são tratados exatamente da mesma forma com a notação padrão “ponto”.
Comparação de XPath e Object Approaches
Qual é a melhor abordagem para acessar dados XML? A Tabela 1 ajuda-o a responder a esta pergunta. A abordagem de objetos é normalmente mais concisa (por exemplo, linha 3), mas nem sempre (linha 4). XPath, contudo, é mais expressiva, na medida em que permite especificar alguns selectores que não são possíveis com notação de objectos (linha 7). No entanto, as próprias capacidades do PowerShell podem facilmente preencher esta lacuna ao usar a notação de objectos (linha 8).
Table 1 Comparando as abordagens XPath e selector de objectos para acesso XML.
Modificar ou Criar Dados XML
Dando uma apreciação dos selectores XML das secções anteriores, modificar um documento XML é bastante simples porque os selectores XML (seja XPath ou objecto) são valores L, ou seja, pode escrever para eles, bem como ler a partir deles! Assim qualquer um destes modificará o autor do 6º livro:
1
2
|
$xdoc.SelectSingleNode(“//book/autor”).InnerText = ‘jones’
$xdoc.catalog.book.author = ‘smith’
|
Bastante frequentemente, você pode ter um arquivo XML existente onde você quer alterar um ou valores de nó. Você provavelmente gostaria de ler o arquivo, modificar os dados e salvar o arquivo de volta para o mesmo nome. Você já viu os dois primeiros passos; o terceiro passo é feito com o método Save no XmlDocument. Colocando tudo junto, então, produz este código básico:
Que o código corre bem – exceto na maioria das vezes, ele parecerá ter falhado! Este simples pedaço de código ilustra uma noção aparentemente menor mas importante do PowerShell; a ignorância disto levou a muitos blogs a afirmar que se trata de um bug no PowerShell. O problema é que seu diretório de trabalho e sua localização no PowerShell não são a mesma coisa. O código acima lê o arquivo muito bem, ele modifica os dados muito bem, mas não necessariamente salva o novo arquivo onde você espera que ele fique. Get-Content, sendo um cmdlet PowerShell, vê um caminho de arquivo relativo à localização do PowerShell. O método XmlDocument.Save, por outro lado, vê um caminho de arquivo relativo ao diretório de trabalho do processo PowerShell porque essa chamada de método está fora do PowerShell. Se você não executou Set-Location (ou seu alias cd) na sua sessão PowerShell atual, ambos apontam para o mesmo diretório. Para confirmar isto, execute estas duas declarações:
1
2
|
Get-Location # mostra a localização do PowerShell
:CurrentDirectory # mostra o directório de trabalho
|
A abordagem mais segura, então, é usar caminhos absolutos para evitar completamente este problema. Veja o artigo de Alex Angelopoulos Why the PowerShell Working Directory and the PowerShell Location Areen’t One in the Same for more.
Adicionar dados XML
Adicionar novos nós ao seu documento XML requer um pouco mais de trabalho do que modificar o valor de um nó existente. Uma abordagem que eu gosto é da entrada do blog de Tobias Weltner Write, Add and Change XML Data: pegue um nó existente do tipo que você deseja criar, faça uma cópia desse nó e modifique a cópia com seus novos dados, e finalmente insira o nó copiado em seu XML como um irmão do original. Aplicando isso ao nosso exemplo de catálogo de livros, este pedaço de código cria um novo nó de livro e adiciona-o ao final da coleção:
Se você deseja adicionar o novo livro em um local diferente, digamos após o 3º livro, use InsertAfter ao invés de AppendChild:
1
|
$xdoc.catalog.InsertDepois($book,$xdoc.catalog.book)
|
(O nome do método InsertDepois desmente o fato de que ele não apenas adiciona nós; ele pode mover nós, também! O método verifica se o nó que você está pedindo para adicionar já está no documento. Se sim, ele o move para o novo local que você especificar. Assim, quando você quer copiar nós, você deve começar com o método Clone como ilustrado acima.)
Para mais exploração sobre manipulação de dados XML com métodos .NET, veja Processar Dados XML Usando o Modelo DOM em MSDN.
Usando XML para Serialização de Objetos
PowerShell fornece uma maneira fácil de persistir objetos usando Export-Clixml para serializar qualquer objeto e armazená-lo em um arquivo XML e Import-Clixml para restaurar o objeto do XML. Com XML, ao contrário da maioria das outras técnicas de serialização, a integridade do objeto é preservada: ao restaurar um objeto de XML todas as propriedades são devidamente digitadas, assim como o próprio objeto pai, de modo que todos os métodos no objeto original também estão disponíveis no objeto regenerado. Para usar o Export-Clixml, simplesmente pipeie qualquer coleção de objetos para ele e especifique um arquivo de destino. Aqui está um exemplo simples mostrando que a saída do Get-ChildItem, uma coleção de objetos FileSystemInfo, é regenerada: