Ei, Equipe de Scripts!Famosas últimas palavras

A Equipe de Scripts da Microsoft

O famoso filósofo grego Sócrates (que morreu no ano 399 a. C., pouco antes do nascimento do Editor de Scripts), é conhecido por ter dito que "Uma vida sem exame não merece ser vivida." A maioria das pessoas já ouviu esse ditado. Pode parecer incrível mas, como a Equipe de Scripts descobriu recentemente, Sócrates nunca disse que "Uma vida sem exame não merece ser vivida". Ao que parece, isso foi uma tradução equivocada feita por um escriba medieval há centenas de anos. Na verdade, Sócrates disse: "Um arquivo XML sem exame não merece ser criado".

Afinal, um ditado filosófico que realmente faz sentido! O XML tem se tornado cada vez mais popular na atualidade; uma rápida pesquisa em um dos computadores de teste da Equipe de Scripts descobriu mais de 500 arquivos XML usados por vários sistemas ou aplicativos.

Nem é preciso dizer que uma enorme quantidade de dados muito importantes é armazenada agora no formato XML. E não há nenhum problema nisso. A menos, é claro, que os dados não sejam examinados. E, infelizmente, em geral é isso o que ocorre, por um simples motivo: a maioria das pessoas nem imagina como examinar um arquivo XML. Especialmente, as pessoas não têm idéia de como consultar e pesquisar esse tipo de arquivo.

Observação Se você não está bem informado sobre os filósofos gregos, Sócrates foi um grande defensor da prática de sair por aí falando sobre as coisas, mas era muito menos entusiasta quanto a sair por aí fazendo coisas. Na verdade, sua esposa, Xantipa, o chamava de "ocioso inútil". Percebe-se por que a Equipe de Scripts tem uma predileção especial por Sócrates.

Claro que alguns leitores devem estar pensando: “Espere um pouco: a Equipe de Scripts não abordou a pesquisa em um arquivo XML em uma coluna anterior ("Perseguindo carros… e XML", em technet.microsoft.com/magazine/cc162506)? Nesse caso, por que estão revisitando esse assunto agora? Será que a Equipe de Scripts é tão preguiçosa a ponto de escrever a mesma coluna várias vezes?"

Acredite ou não, somos ainda mais preguiçosos. Mas esse não é nosso motivo principal para revisitar o assunto. Na coluna anterior, falamos sobre o trabalho com um arquivo XML estruturado como o código na Figura 1, com cada valor de propriedade representando um único nó do arquivo.

Figura 1. Arquivo XML somente com nós

<?xml version='1.0'?> 
  <INVENTORY> 
    <COMPUTER>
      <os>Windows XP</os>
      <department>Human Resources
      </department>
      <name>atl-ws-001</name>
    </COMPUTER> 
    <COMPUTER>
      <os>Windows XP</os>
      <department>Finance</department>
      <name>atl-ws-002</name>
    </COMPUTER> 
    <COMPUTER>
      <os>Windows Vista</os>
      <department>Finance</department>
      <name>atl-ws-003</name>
    </COMPUTER> 
  </INVENTORY>

Muito bem, exceto pelo fato — que as pessoas rapidamente identificaram e sobre o qual nos escreveram — de que essa não é a única forma na qual é possível estruturar arquivos XML. No exemplo anterior, cada computador no arquivo tem seu próprio nó; cada um desses nós, por sua vez, tem determinado número de nós filho (os, department e name). Contudo, também é possível construir um arquivo XML em que os nós individuais não tenham nós filho. Em vez disso, valores adicionais de propriedades são configurados como atributos. Como no arquivo mostrado na Figura 2.

Figura 2. Arquivo XML com atributos

<?xml version='1.0'?> 
  <HARDWARE> 
      <COMPUTER os="Windows XP" department="Human Resources">atl-ws-001</COMPUTER> 
      <COMPUTER os="Windows XP" department="Finance">atl-ws-002</COMPUTER> 
      <COMPUTER os="Windows Server 2003" department="IT">atl-fs-003</COMPUTER> 
      <COMPUTER os="Windows Vista" department="IT">atl-ws-004</COMPUTER> 
      <COMPUTER os="Windows Vista" department="Human Resources">atl-ws-005</COMPUTER> 
      <COMPUTER os="Windows Vista" department="Finance">atl-ws-006</COMPUTER> 
      <COMPUTER os="Windows XP" department="Sales">atl-ws-007</COMPUTER> 
      <COMPUTER os="Windows Server 2008" department="IT">atl-fs-008</COMPUTER> 
      <COMPUTER os="Windows XP" department="Human Resources">atl-ws-009</COMPUTER> 
      <COMPUTER os="Windows Vista" department="Sales">atl-ws-010</COMPUTER> 
  </HARDWARE>

Isso é um problema? Na verdade, sim. O script que mostramos há muito tempo, em uma edição distante, não funciona com um arquivo XML estruturado como aquele mostrado na Figura 2. Simplesmente não dá resultado. E isso é definitivamente um problema, porque muitos de vocês precisam ler esse tipo de arquivo XML.

Tudo o que vocês precisaram fazer foi perguntar. Bem... Além de esperar um ano e meio até que finalmente resolvêssemos tratar do assunto.

Antes de prosseguir, vamos analisar melhor nosso arquivo XML. Neste caso, temos um arquivo com o nó principal chamado HARDWARE; cada computador individual existe como nó filho de HARDWARE. Além disso, cada um desses nós tem dois atributos: os (usado para armazenar o nome do sistema operacional instalado no computador) e department (usado para armazenar o nome do departamento proprietário do computador).

Suponha, por exemplo, que quiséssemos obter uma lista de todos os computadores executando Windows XP. É possível fazer isso usando um script? Claro que sim:

Set xmlDoc = _
  CreateObject("Microsoft.XMLDOM")

xmlDoc.Async = "False"
xmlDoc.Load("HARDWARE.xml")

Set colNodes=xmlDoc.selectNodes _
  ("//HARDWARE/COMPUTER[@os='Windows XP']")

For Each objNode in colNodes
  Wscript.Echo objNode.Text 
Next

Vejamos o que temos aqui. Para começar, criamos uma instância do objeto Microsoft.XMLDOM; como o nome já diz, esse é o objeto que nos permite trabalhar com arquivos XML. Depois de criar o objeto, precisamos definir a propriedade Async como False. Isso permite ao script saber que desejamos carregar o documento de forma síncrona, em vez de assíncrona. Qual a importância disso para o script? Na verdade, nenhuma. Afinal, os scripts (assim como os membros da Equipe de Scripts) são objetos inanimados.

Mas isso é importante para você. Se carregássemos o documento de forma assíncrona, o script continuaria em execução, mesmo que o documento não estivesse totalmente carregado. Isso não seria nada bom: imagine os problemas que poderiam ocorrer se você tentasse realizar uma tarefa em um documento que ainda não existisse. Carregar um arquivo XML de forma síncrona garante que ele será totalmente carregado antes que o script prossiga.

Por uma incrível coincidência, chegamos ao ponto em que devemos carregar nosso arquivo XML. Fazemos isso chamando o método Load e abrindo o arquivo C:\Scripts\HARDWARE.xml, o que nos traz a esta linha de código:

Set colNodes=xmlDoc.selectNodes _
  ("//HARDWARE/COMPUTER[@os='Windows XP']")

O que estamos fazendo aqui é usar o método SelectNodes para consultar o arquivo XML e informar ao script quais nós XML queremos recuperar. Observe a sintaxe, que é, sem dúvida, um pouco fora do comum.

Para começar, precisamos especificar o caminho no arquivo XML. Nosso arquivo XML tem um nó de nível superior chamado HARDWARE, seguido por uma série de nós de segundo nível chamados COMPUTER. Cada um desses nós de segundo nível representa um único registro em nosso arquivo de dados XML. Por isso, nossa consulta selectNodes começa assim:

//HARDWARE/COMPUTER

Neste ponto, encontramos a seguinte construção:

[@os='Windows XP']

Essa parte da consulta é semelhante a uma cláusula WHERE em uma consulta SQL padrão. Com o SQL, poderíamos usar uma consulta ao banco de dados semelhante a essa para recuperar uma lista de todos os computadores executando o sistema operacional XP:

SELECT Name FROM Hardware 
    WHERE OS = 'Windows XP'

Com o método SelectNodes, estamos fazendo algo semelhante: estamos pedindo para obter uma lista de todos os computadores nos quais o atributo os (@os) é igual a Windows XP. E, para indicar que se trata de uma cláusula WHERE, colocamos toda a cláusula entre colchetes.

Observação Esse tipo de consulta é conhecido como XPath. Para saber mais sobre o XPath, consulte msdn.microsoft.com/library/ms256115.aspx.

Como dissemos, é um pouco estranho, mas funciona. E se quiséssemos obter uma lista de todos os computadores pertencentes ao departamento Finance? Não seria difícil. Nossa chamada para selectNodes teria esta aparência:

Set colNodes=xmlDoc.selectNodes _
  ("//HARDWARE/COMPUTER" & _
   "[@department='Finance']")

Mais uma vez, a cláusula WHERE é colocada entre colchetes, o nome do atributo (department) é precedido pelo sinal de arroba (@), e então indicamos o valor no qual estamos interessados.

Simples, não é?

Depois de chamarmos o método selectNodes, podemos recuperar os nomes dos computadores simplesmente fazendo um loop pela coleção de valores retornados e recuperando a propriedade Text, conforme mostrado a seguir:

For Each objNode in colNodes
  Wscript.Echo objNode.Text 
Next

Que tipo de informação podemos esperar obter? Para dizer a verdade, esperamos obter informações como estas:

atl-ws-001
atl-ws-002
atl-ws-007
atl-ws-009

Ou seja, os nomes de todos os computadores ainda executando Windows XP.

Aliás, você não está limitado a recuperar apenas o nome do computador. Como o nome do computador é o valor "padrão" para cada nó, isso é simplesmente o que obtemos ao fazer referência à propriedade Text.

Como alternativa, seria possível especificar exatamente quais valores de atributos gostaríamos que fossem retornados. Observe, por exemplo, este loop For Each modificado:

For Each objNode in colNodes
    Wscript.Echo objNode.Text 
    Wscript.Echo objNode.Attributes. _
      getNamedItem("department").Text
    Wscript.Echo
Next

Como você pode ver, ainda estamos recuperando no loop o valor da propriedade Text. No entanto, incluímos também esta linha de código:

Wscript.Echo objNode.Attributes. _
  getNamedItem("department").Text

Nesse caso, usamos o método getNamedItem para recuperar o valor do atributo department; depois, recuperamos a propriedade Text para esse atributo. Assim, podemos especificar os valores de atributos que queremos (ou não) recuperar e exibir na tela (observe também que adicionamos um comando Wscript.Echo para colocar uma linha em branco entre os registros). Ao executarmos esse script, devemos obter o seguinte:

atl-ws-001
Human Resources

atl-ws-002
Finance

atl-ws-007
Sales

atl-ws-009
Human Resources

E, se você quiser, poderá escrever consultas mais complexas. Suponha, por exemplo, que você queira uma lista de todos os computadores executando Windows XP ou Windows Vista. No SQL, você faria isso escrevendo uma consulta OR. Adivinhe! Você deve fazer exatamente o mesmo para consultar um arquivo XML:

Set colNodes=xmlDoc.selectNodes _
    ("//HARDWARE/COMPUTER" & _
     "[@os='Windows XP' or " & _
     "@os='Windows Vista']")

Viu o que fizemos aqui? Entre um único par de colchetes, fornecemos dois critérios: @os='Windows XP' ou @os='Windows Vista'. Basta fazer isso para recuperar um relatório parecido com o da Figura 3.

Figura 3. Resultado da consulta OR

atl-ws-001
Human Resources

atl-ws-002
Finance

atl-ws-004
IT

atl-ws-005
Human Resources

atl-ws-006
Finance

atl-ws-007
Sales

atl-ws-009
Human Resources

atl-ws-010
Sales

Se você verificar o arquivo XML, perceberá que os computadores executando Windows XP ou Windows Vista estão incluídos na saída; já os computadores executando Windows Server 2003 ou Windows Server 2008, não. Nem deveriam estar.

Alguma dúvida? Você gostaria de escrever uma consulta mais restrita, que retornasse somente os computadores executando Windows XP e pertencentes ao departamento Finance? Como você já deve imaginar, isso é fácil. Basta escrever uma consulta AND desta forma:

Set colNodes=xmlDoc.selectNodes _
    ("//HARDWARE/COMPUTER" & _
     "[@os='Windows XP' and " & _
     "@department='Finance']")

Isso retornará o seguinte conjunto de dados:

atl-ws-002
Finance

Por que há apenas um item no relatório? Isso mesmo: porque o departamento Finance tem um único computador executando Windows XP.

Esperamos ter ajudado todos vocês a lidar com esse tipo específico de arquivo XML. Se, no fim das contas, ainda houver outro tipo de arquivo XML escondido por aí, bem... Boa sorte.

Por termos começado a coluna deste mês com um ditado famoso, imaginamos que seria adequado encerrá-la no mesmo tom. Então, gostaríamos de terminar com as palavras do Kaiser Guilherme II, pelas quais a Equipe de Scripts tem grande apreço: "Por meio deste, renuncio perpetuamente às reivindicações ao trono da Prússia, bem como ao trono do Império Alemão, a ele conectado".

O desafio de script do Dr. Scripto

O desafio mensal que testa não apenas sua habilidade de resolver quebra-cabeças, mas também de criar scripts.

Outubro de 2008: palavras cruzadas de VBScript

Para resolver este quebra-cabeça, basta preencher cada linha com o nome de uma função do VBScript. Quando terminar, coloque as letras nos quadrados azuis na ordem correta para descobrir o nome de mais uma função.

fig10.gif

RESPOSTA:

O desafio de script do Dr. Scripto

Resposta: outubro de 2008: palavras cruzadas de VBScript

puzzle_answer.gif

A Equipe de Scripts — Greg Stemp e Jean Ross — trabalha na Microsoft.