Não encontrou o que procurava? Dê uma olhada nessas páginas!

Blog

Como gerar um arquivo XML de fácil leitura?

14 de Dezembro de 2013
Por Vincent Oorsprong

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?

Knowledge Base

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

A Microsoft tem a classe!

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:

Importar a biblioteca MSXML6.0 COM

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

Convertendo o código do VB para código do DataFlex

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.


Procedure WriteXMLFormatted Global String sFile
    Handle hoReader hoWriter
    Variant vWriter vData
    Get Create (RefClass (cComSAXXMLReader60)) to hoReader
    Send CreateComObject of hoReader
    Get Create (RefClass (cComMXXMLWriter60)) to hoWriter
    Send CreateComObject of hoWriter
    Get pvComObject of hoWriter to vWriter
    Set ComStandalone of hoWriter to True
    Set ComByteOrderMark of hoWriter to False
    Set ComEncoding of hoWriter to "utf-8"
    Set ComIndent of hoWriter to True
    Set ComOmitXMLDeclaration of hoWriter to True
    Set ComContentHandler of hoReader to vWriter
    Set ComDtdHandler of hoReader to vWriter
    Set ComErrorHandler of hoReader to vWriter
    Send ComPutProperty of hoReader "http://xml.org/sax/properties/lexical-handler" vWriter
    Send ComPutProperty of hoReader "http://xml.org/sax/properties/declaration-handler" vWriter
    Get ComOutput of hoWriter to vData
    Send ComParseURL of hoReader sFile
    Send ReleaseComObject of hoWriter
    Send ReleaseComObject of hoReader
    Send Destroy of hoReader
    Send Destroy of hoWriter
End_Procedure

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:

xml raw

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 está OK?

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.

Objeto Stream

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. Can’t 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.

Write to Disk

Com a mensagem ComSaveToFile, as informações convertidas do objeto Stream serão gravadas em disco.


Send ComSaveToFile of hoStream sFile OLEadSaveCreateOverWrite
Send ComClose of hoStream

Adicione o código acima depois da instrução ComParseURL.

Chinês?

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:

google translated

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:

converted pretty

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!