Este blog trata de como tornar um XML mais legível. Deste 1999 o DataFlex é capaz de trabalhar com XML através de classes definidas no pacote FleXML.pkg (cXMLDomDocument, etc.). Uma das necessidades mais comuns que recebemos é como fazer com que um XML seja mais legível para nós, humanos, já que a Microsoft gera dados XML como uma grande linha de texto. Como a maioria dos desenvolvedores preferem ler o conteúdo XML em um editor de texto, você pode facilmente formatar o arquivo XML com ferramentas como o Notepad ++, porém como fazer isso com o seu próprio código?
Como mencionado, esta é uma pergunta que recebemos frequentemente, portanto em 2002 eu escrevi um artigo para o Knowledge Base chamado "Add formatting to an XML file".
Na época em que eu escrevi o artigo para o Knowledge Base, não havia nenhuma outra solução, mas com novas versões do MSXQL, existe uma forma de fazer isso. Eu encontrei esta informação em uma postagem no Stackoverflown chamada Forcing MSXML to format XML output with indents and newlines. Depois de alguns testes, eu pude fazer isso funcionar no DataFlex. Os passos necessários para implementar essa funcionalidade estão abaixo:
O artigo utiliza os objetos MXXMLWriter e SAXXMLReader, os quais são definidos na biblioteca de objetos Microsoft XML 6.0. Isso significa que você precisa gerar a classe COM, importando a biblioteca MSXML6.0. Para isso, no Studio, vá até "File | New | Class | Import COM Automation" e procure "Microsoft XML, v.6.0 (version 6.0)" na lista. Se você selecionar essa opção, você verá que o Studio criará um pacote chamado MSXML6.pkg na sua workspace.
Se você compilar este pacote na sua aplicação, você receberá dois erros de compilação que são corrigidos ao adicionar a seguinte linha de código ANTES do comando USE utilizado para inserir o pacote.
Define OLE_VT_UI8 for 21
Não é tão difícil converter o código para leitura e interpretação de XML para DataFlex. Você precisa criar objetos das classes cComSAXXMLReader60 e cComMSXMLWriter60. No código a seguir, eles são criados através da função Create. Porque estas classes são classes de automação, você pode criar o objeto DataFlex, porém os objetos COM não serão criados (a propriedade peAutoCreate controla essa parte e o padrão é acNoAutoCreate). Isso significa que você precisa enviar a mensagem CreateComObject para os objetos DataFlex. Depois disto, você pode obter o Dispatch ID do objeto Writer para conectar o Writer e o Reader.
Não copie e cole o código acima, pois ele não está completo ainda. Pegue um arquivo XML que não seja tão legível, como o arquivo de filtro do Database Explorer, que se parece com a imagem abaixo:
Adicione uma linha de código para utilizar este arquivo:
Send WriteXMLFormatted "C:\Order Entry\Data\vendor.DBE-Filter"
A procedure WriteXMLFormatted não escreve o conteúdo no disco, ele joga a informação numa variável string. Não existe uma função para gravar tudo isto em disco. A ComOutput pode ser obtida para uma string, como o código atual faz, ou conectada a um objeto Stream ou XMLDomDocument. Você provavelmente não utilizará o XMLDomDocument porque ele destruirá a formatação do XML.
Uma string? Isto está OK? Não, não está. Existem dois problemas ao utilizarmos uma string.
A string é limitada pelo tamanho do argumento do DataFlex (65k por padrão), o que significa que mais dados podem ser truncados e resultar num arquivo XML inválido. Assim que a string é escrita em disco com o comando DataFlex Write, ela será convertida de UNICODE para OEM.
Se os dados XML forem menores que 65k e não houver problemas com a conversão para OEM no seu ambiente, você pode utilizar os comandos padrões Direct_Output, Write e Close_Output.
Por causa das limitações da string, você deve dar uma olhada no objeto Stream. Como eu crio tal objeto? Ele não está disponível por padrão no DataFlex, já que temos comandos de E/S sequenciais na linguagem há mais de 35 anos.
O código de exemplo mostra o uso de um objeto da classe ADODB.Stream. Se você for novamente até a opção "Import COM Automation" (como fizemos para a classe MSXML6) e procurá-la na lista, você não vai encontrá-la. A solução é clicar no botão "Browse ..." localizado na parte inferior da janela. Feito isto, a janela Common File Dialog será aberta e você pode navegar até o arquivo da biblioteca COM. Você precisa encontrar e selecionar o arquivo MSADO60.TLB (type library). No meu computador Windows 8 (64 bits), este arquivo está localizado em: C:\Program Files (x86)\Common Files\System\ado\msado60.tlb.
Assim que o pacote for criado, adicione o código para criar o objeto Stream:
Get Create (RefClass (cComStream)) to hoStream
Send CreateComObject of hoStream
Get pvComObject of hoStream to vStream
O Dispatch ID armazenado em vStream precisa ser utilizado com o método ComOutput do objeto Writer, portanto substitua:
Get ComOutput to vData
por:
Set ComOutput to vStream
Note que há duas alterações na linha de código!
Tudo pronto agora? Não. Se utilizarmos o código que temos, receberemos um erro "COM object method invocation error. Cant save". Recebemos este erro porque o objeto Stream ainda não está aberto. O código a seguir precisa ser adicionado.
Send ComOpen of hoStream Nothing OLEadModeUnknown OLEadOpenStreamUnspecified
Se você presquisar sobre o método OPEN na documentação do MSDN, você verá que os parâmetros UID e PWD são opcionais, porém se você passar NOTHING, você receberá um erro de COM do objeto Stream, pois ele não aceita isso. Por esse motivo, eu passei duas strings vazias. Na verdade, se eu passar strings com valores (usuário e senha), eu receberei o mesmo erro.
Com a mensagem ComSaveToFile, as informações convertidas do objeto Stream serão gravadas em disco.
Adicione o código acima depois da instrução ComParseURL.
Agora execute o seu código, mas não utilize um arquivo que você não possa destruir! O arquivo gerado se parece com o seguinte:
Aparentemente isso é causado por causa de uma configuração que esquecemos de fazer. Esquecemos de configurar a propriedade Type. O padrão é adTypeText, o qual depende do atributo CharSet. Se você alterar o tipo, você não verá este "problema". Portanto, adicione:
Set ComType of hoStream to OLEadTypeBinary
O resultado será o seguinte:
Você também deve remover a instância Stream através das mensagens ReleaseComObject e Destroy. Assim todos os objetos COM são liberados e os objetos do DataFlex são destruídos.
Espero que vocês tenham aprendido algo novo neste blog e também encontrá-los na próxima conferência!