XML Tutorial/pt

From Lazarus wiki
Revision as of 15:13, 24 March 2012 by Vincent (talk | contribs) (Text replace - "delphi>" to "syntaxhighlight>")
Jump to navigationJump to search

Deutsch (de) English (en) español (es) français (fr) magyar (hu) Bahasa Indonesia (id) italiano (it) 日本語 (ja) 한국어 (ko) português (pt) русский (ru) 中文(中国大陆)‎ (zh_CN)

Introdução

A "Extensible Markup Language" é uma linguagem recomendada pela W3C criada para a troca de informações entre diferentes sistemas. É um formato baseado em texto para armazenar informações. Linguagens modernas de troca de dados, como XHTML, bem como a maioria das tecnologia de WebServices, são baseados no XML.

Atualmente há um conjunto de units que fornecem suporte para o XML no Free Pascal. Estas units são chamadas "XMLRead", "XMLWrite" e "DOM" e elas são parte da Biblioteca de Componentes Livre (FCL) do compilador Free Pascal. A FCL já esta presente no caminho de busca (search path) padrão para o compilador no Lazarus, assim você só precisa adicionar as units na claúsula uses para ter suporte ao XML. A FCL não está com sua documentação atualizada (Outubro/2005), assim este pequeno tutorial é uma introdução ao acesso a XML usando estas units.

O XML DOM (Modelo Objeto de Documento) é um conjunto objetos padronizados que fornece uma interface similar para uso em diferentes linguagens e sistemas. O padrão só especifica os métodos, propriedades e outras partes da interface do objeto, deixando a implementação liberada para diferentes linguagens. A FCL atualmente tem suporte completo a XML DOM 1.0.

Exemplos

A seguir tem uma lista de exemplos de manipulação de dados XML com complexidade crescente.

Lendo um nó de texto (text node)

Para programadores Delphi: Note que, quando trabalha-se com TXMLDocument, o texto dentro de um Nó é considerado um nó de TEXTO separado. Como resultado, você deve acessar o valor do

texto do nó como um nó separado. Como alternativa, a propriedade TextContent pode ser usada para extrair o conteúdo de todos os nós de textos abaixo do

nó dado, com ambos concatenados.

A procedure ReadXMLFile sempre cria um novo TXMLDocument, assim você não tem que criá-lo com antecedência. Entretanto, tenha certeza de destruir

o documento chamando Free quando você terminar.

Por exemplo, considere o seguinte XML:

 <?xml version="1.0"?>
 <request>
   <request_type>PUT_FILE</request_type>
   <username>123</username>
   <password>abc</password>
 </request>

O seguinte exemplo de código mostra ambas as formas (correta e incorreta) de pegar o valor do nó de texto:

 var
  PassNode: TDOMNode;
  Doc:      TXMLDocument;
 begin
  // Lê no arquivo xml no disco
  ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');
  // Extrai o nó "password"
  PassNode := Doc.DocumentElement.FindNode('password');
  // Escreve por extenso o valor do nó selecionado
  WriteLn(PassNode.NodeValue); // estará em branco
  // O texto do nó é atualmente um "nó filho" separado
  WriteLn(PassNode.FirstChild.NodeValue); // mostra corretamente "abc"
  // como alternativa
  WriteLn(PassNode.TextContent);
  // finalmente, libera o documento
  Doc.Free;
end;

Mostrando os nomes dos nós

Uma nota rápida na navegação pela árvore DOM: Quando você precisar acessar nós em seqüencia, é melhor usar as propriedades FirstChild e

NextSibling (para métodos GetElementsByTagName, mas esses serão criados um objeto TDOMNodeList que deverá ser eventualmente liberado. Isso difere

das outras implementações DOM como MSXML, porque a implementação feita pela FCL é baseada em objeto, não baseada na interface (interface-based).

Os seguintes exemplos demonstram como mostrar os nomes dos nodes para um TMemo localizado em um formulário.

A seguir o arquivo XML chamado 'C:\Programas\teste.xml':

 <?xml version="1.0"?>
 <images directory="mydir">
  <imageNode URL="graphic.jpg" title="">
    <Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca>
    <Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca>
  </imageNode>
 </images>

E aqui o código Pascal para executar a tarefa:

 var
   Documento: TXMLDocument;
   Child: TDOMNode;
   j: Integer;
 begin
   ReadXMLFile(Documento, 'C:\Programas\teste.xml');
   Memo.Lines.Clear;
   // usando as propriedades FirstChild e NextSibling
   Child := Documento.DocumentElement.FirstChild;
   while Assigned(Child) do
   begin
     Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);
     // usando método ChildNodes
     with Child.ChildNodes do
     try
       for j := 0 to (Count - 1) do
         Memo.Lines.Add(Item[j].NodeName + ' ' + Item[j].FirstChild.NodeValue);
     finally
       Free;
     end;
     Child := Child.NextSibling;
   end;
   Documento.Free;
 end;

Isso mostrará:

imageNode graphic.jpg
Peca Pecacastelo.jpg1.swf
Peca Pecacastelo.jpg1.swf

Povoando um TreeView com XML

Um uso comum de arquivos XML é analisá-los e mostrar seu conteúdo em uma árvore como formato. Você pode encontrar o componente TTreeView na aba/guia "Common

Controls" do Lazarus.

A função a seguir pegará um documento XML, previamente carregado de um arquivo ou genrado em código, e povoará um TreeView com seu conteúdo. O caption de

cada nó consistirá no conteúdo do primeiro atributo de cada nó.

procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);
var
  iNode: TDOMNode;

  procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);
  var
    cNode: TDOMNode;
  begin
    if Node = nil then Exit; // Pára, se atingir a folha
    
    // Adiciona um nó para a árvore
    TreeNode := tree.Items.AddChild(TreeNode, Node.Attributes[0].NodeValue);

    // Vai para o primeiro nó filho
    cNode := Node.FirstChild;

    // Processa todos os nós filhos
    while cNode <> nil do
    begin
      ProcessNode(cNode, TreeNode);
      cNode := cNode.NextSibling;
    end;
  end;
    
begin
  iNode := XMLDoc.DocumentElement.FirstChild;
  while iNode <> nil do
  begin
    ProcessNode(iNode, nil); // Recursivo
    iNode := iNode.NextSibling;
  end;
end;

Modificando um documento XML

A primeira coisa a ser lembrada é que TDOMDocumento é o "handle" para o DOM. Você pode pegar uma instância dessa classe criando uma ou carregando um

documento XML.

Nós, por outro lado, não podem ser criados como um objeto normal. Você *deve* usar os métodos fornecidos por TDOMDocumento para criá-los, e posteriormente

usar outros métodos para colocá-los no lugar correto na árvore. Isso ocorre porque os nós devem ser "pertencentes" a um documento específico no DOM.

A seguir alguns métodos comuns de TDOMDocument:

   function CreateElement(const tagName: DOMString): TDOMElement; virtual;
   function CreateTextNode(const data: DOMString): TDOMText;
   function CreateCDATASection(const data: DOMString): TDOMCDATASection;
     virtual;
   function CreateAttribute(const name: DOMString): TDOMAttr; virtual;

E aqui um método de exemplo que localizará o item selelcionado em um TTreeView e inseri-rá um nó filho para o documento XML que ele representa. O TreeView

deve ser previamente preenchido com o conteúdo de um arquivo XML usando a função XML2Tree.

procedure TForm1.actAddChildNode(Sender: TObject);
var
  Posicao: Integer;
  NovoNo: TDomNode;
begin
  {*******************************************************************
  *  Detecta o elemento selecionado
  *******************************************************************}
  if TreeView1.Selected = nil then Exit;

  if TreeView1.Selected.Level = 0 then
  begin
    Posicao := TreeView1.Selected.Index;

    NovoNo := XMLDoc.CreateElement('item');
    TDOMElement(NovoNo).SetAttribute('nome', 'Item');
    TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo');
    with XMLDoc.DocumentElement.ChildNodes do
    begin
      Item[Posicao].AppendChild(NovoNo);
      Free;
    end;

    {*******************************************************************
    *  Atualiza o TreeView
    *******************************************************************}
    TreeView1.Items.Clear;
    XML2Tree(TreeView1, XMLDoc);
  end
  else if TreeView1.Selected.Level >= 1 then
  begin
    {*******************************************************************
    *  Essa função funciona somente no primeiro nível da árvore,
    *  mas pode facilmente ser modificada para funcionar em quaisquer outros níveis
    *******************************************************************}
  end;
end;

Criar um TXMLDocument de um string

Dado um arquivo XML em MyXmlString, o seguinte código criará esse DOM:

Var
  S : TStringStream;
  XML : TXMLDocument;

begin
  S:= TStringStream.Create(MyXMLString);
  Try
    S.Position:=0;
    XML:=Nil;
    ReadXMLFile(XML,S); // Completa o documento XML
    // Alternativamente:
    ReadXMLFragment(AParentNode,S); // Lê somente um fragmento XML.
  Finally
    S.Free;
  end;
end;

Validando um documento

Desde Março de 2007, DTD validation facility foi adicionado para o analisador XML da FCL. A validação é a verificação da estrutura lógica do documento

conforme as regras pré-definidas, chamadas Documento Type Definition (DTD).

Aqui está um exemplo de um documento XML com um DTD:

  <?xml version='1.0'?>
  <!DOCTYPE root [
  <!ELEMENT root (child)+ >
  <!ELEMENT child (#PCDATA)>
  ]>
  <root>
    <child>This is a first child.</child>
    <child>And this is the second one.</child>
  </root>

Esse DTD especifica que o elemento 'root' (raiz) deve ter um ou mais elementos 'child' (filho), e que os elementos 'child' pode ter somente dados de

caractere dentro. Se a análise deteca quaisquer violação dessas regras, ela reportará essas violações.

Carregar o documento dessa forma é um pouco mais complicado. Assumiremos que temos dados XML em um objeto TStream:

procedure TMyObject.DOMFromStream(AStream: TStream);
var
  Parser: TDOMParser;
  Src: TXMLInputSource;
  TheDoc: TXMLDocument;
begin
  // cria um objeto analisador
  Parser := TDOMParser.Create;
  // e a fonte de entrada
  Src := TXMLInputSource.Create(AStream);
  // nós queremos a validação
  Parser.Options.Validate := True;
  // associa um manipulador de erro que receberá as notificações
  Parser.OnError := @ErrorHandler;
  // agora faz o trabalho
  Parser.Parse(Src, TheDoc);
  // ...e limpeza total
  Src.Free;
  Parser.Free;
end;

procedure TMyObject.ErrorHandler(E: EXMLReadError);
begin
  if E.Severity = esError then  // nós estamos interessados somente em erros de validação
    writeln(E.Message);
end;

Gerando um arquivo XML

Abaixo um código completo para escrever em arquivo XML. Você pode ler o tutorial completo no blog da DeveLazarus (veja Links Externos) Por favor, lembre das bibliotecas DOM e XMLWrite na cláusula uses

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,
  DOM, XMLWrite;

type
  { TForm1 }
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;
  
var
  Form1: TForm1;
  
implementation

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  xdoc: TXMLDocument;                                  // variável para o documento
  noraiz, nopai, nofilho: TDOMNode;                    // variáveis dos nós
begin
  //cria um documento
  xdoc := TXMLDocument.create;

  //cria nó raiz
  noraiz := xdoc.CreateElement('cadastro');
  Xdoc.Appendchild(noraiz);                           // salva nó raiz

  //cria nó pai
  noraiz:= xdoc.DocumentElement;
  nopai := xdoc.CreateElement('usuario');
  TDOMElement(nopai).SetAttribute('id', '001');       // cria atributo para o nó pai
  noraiz.Appendchild(nopai);                          // salva o nó pai

  //cria nó filho
  nopai := xdoc.CreateElement('nome');                // cria o nó filho
  //TDOMElement(nopai).SetAttribute('sexo', 'M');     // cria atributo
  nofilho := xdoc.CreateTextNode('Fernando');         // insere um valor para o nó
  nopai.Appendchild(nofilho);                         // salva nó
  noraiz.ChildNodes.Item[0].AppendChild(nopai);       // insere o nó filho no nó pai correspondente

  //cria nó filho
  nopai := xdoc.CreateElement('idade');               // cria nó filho
  //TDOMElement(nopai).SetAttribute('ano', '1976');   // cria atributo
  nofilho := xdoc.CreateTextNode('32');               // coloca um valor ao nó
  nopai.Appendchild(nofilho);                         // salva o nó
  noraiz.ChildNodes.Item[0].AppendChild(nopai);       // insere o nó filho no nó pai correspondente

  writeXMLFile(xDoc,'teste.xml');                     // escreve o XML
  Xdoc.free;                                          // libera memória
end;

initialization
  {$I unit1.lrs}

end.

Resulta no seguinte arquivo XML:

 
 <?xml version="1.0" ?> 
 - <cadastro>
   - <usuario id="001">
       <nome>Fernando</nome> 
       <idade>32</idade> 
     </usuario>
   </cadastro>

--Fernandosinesio 22:28, 24 April 2008 (CEST)fernandosinesio@gmail.com

Links Externos