Difference between revisions of "XML Tutorial/es"

From Free Pascal wiki
Jump to navigationJump to search
Line 202: Line 202:
 
  function CreateCDATASection(const Datos: DOMString): TDOMCDATASection; virtual;
 
  function CreateCDATASection(const Datos: DOMString): TDOMCDATASection; virtual;
 
  function CreateAttribute(const nombre: DOMString): TDOMAttr; virtual;</delphi>
 
  function CreateAttribute(const nombre: DOMString): TDOMAttr; virtual;</delphi>
 +
&nbsp;&nbsp;&nbsp;CreateElement crea un nodo nuevo.
 +
 +
&nbsp;&nbsp;&nbsp;CreateTextNode crea un valor par un nodo.
 +
 +
&nbsp;&nbsp;&nbsp;CreateAttribute crea un atributo dentro de un nodo.
 +
 +
&nbsp;&nbsp;&nbsp;CreateCDATA crea una sección CDATA: Los caracteres habituales de marca de XML cómo <> no se interpretan dentro de la sección CDATA. Ver [https://secure.wikimedia.org/wikipedia/en/wiki/CDATA artículo sobre CDATA en la Wikipedia]
  
 
&nbsp;&nbsp;&nbsp;Este es un ejemplo de cómo ubicar el elemento seleccionado en un TTreeView e insertar el nodo hijo
 
&nbsp;&nbsp;&nbsp;Este es un ejemplo de cómo ubicar el elemento seleccionado en un TTreeView e insertar el nodo hijo

Revision as of 21:00, 22 August 2011

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)

Introducción

   El Lenguaje de Marcas Extensible (XML) es recomendado por el W3C y fue creado para el intercambio de información entre sistemas diferentes. Utiliza texto para almacenar la información. Lenguajes modernos de intercambio de datos, como XHTML y muchas tecnologías de servicios WEB están basados en XML.

   Actualmente hay un conjunto de unidades que dan soporte a XML en Free Pascal. Estas unidades son "XMLRead", "XMLWrite" y "DOM" y son parte de la FCL del compilador Free Pascal. las unidades de la FCL están en el ruta de búsqueda por defecto del compilador de Lazarus, por lo que sólo tendrá que añadir las unidades a la cláusula uses para utilizar XML. La FCL no está actualmente documentada completamente (Octubre / 2005), por lo que este breve tutorial tiene por objeto realizar una introducción al acceso a ficheros XML utilizando las unidades mencionadas.

   El Modelo de Objeto de Documento (DOM) de XML es un conjunto normalizado de objetos que proporcionan una interfaz similar para el uso de XML en diferentes lenguajes y sistemas. La norma sólo especifica los métodos, propiedades y otras partes de la interfaz del objeto, dejando la implementación libre para los diferentes ilenguajes. El FCL actualmente apoya plenamente DOM XML 1.0.

Ejemplos

   Lo que sigue son ejemplos de manipulación de datos XML con una complejidad creciente. Las unidades necesarias para compilar los códigos de los ejemplos (y para cualquier otro código con XML) son: DOM, XMLRead, XMLWrite, XMLCfg, XMLUtils, XMLStreaming. Aunque no todas son necesarias para todos los ejemplos.

Leyendo un nodo de texto

   Para programadores Delphi:

   Resaltar que cuándo se trabaja con TXMLDocument, el texto en un nodo es considerado un nodo de Texto separado. Por tanto se accede al texto del nodo en un nodo separado. Alternativamente, la propiedad TextContent puede utilizarse para recuperar el contenido de todos los nodos de texto por debajo de uno dado, concatenados todos ellos.

    El procedimiento ReadXMLFile crea siempre un nuevo objeto TXMLDocument, por lo que no hay que crearlo previamente de forma manual. Hay que asegurarse de destruir el documento llamando a Free cuando ya no lo necesitemos.

    Por ejemplo, veamos el siguiente XML:

<xml> <?xml version="1.0"?>

<solicitud>
  <tipo_solicitud>PUT_FILE</tipo_solicitud>
  <usuario>123</usuario>
  <contrasenya>abc</contrasenya>
</solicitud></xml>

   Este ejemplo muestra la forma correcta y la incorrecta para obtener el valor textual de un nodo (no olvides añadir las unidades XMLRead and DOM en la sección uses):

<delphi> var

NodoContra: TDOMNode;
 Doc:      TXMLDocument;
begin
 try
  // leer archivo XMl desde disco
  ReadXMLFile(Doc, 'prueba.xml');
  // Extraer el nodo "contrasenya"
  NodoContra := Doc.DocumentElement.FindNode('contrasenya');
  // Escribir el valor del nodo elegido
  WriteLn(NodoContra.NodeValue); // estará vacío
  // El texto del nodo es un nodo hijo en este momento
  WriteLn(NodoContra.FirstChild.NodeValue); // Presenta "abc", tal como deseábamos
  // alternativamente
  WriteLn(NodoContra.TextContent);
 finally
  // Y para terminar liberar la memoria que ocupa nuestro objeto Doc
  Doc.Free;
end;

end;</delphi>

   Ten en cuenta que ReadXMLFile (...) ignora todos los caracteres de espacios en blanco al analizar un documento. La sección espacios en blanco se describe cómo conservarlos.

Imprimir los nombres de los nodos y atributos

   Brevemente, cómo recorrer el árbol DOM: Cuándo es necasario recorrer los nodos secuencialmente, lo mejor es utilizar las propiedades FirstChild y NextSibling, para avanzar en el árbol y las propiedades LastChild y PreviousSibling para recorrer el árbol de forma inversa. para acceder de forma aleatoria a nodos podemos utilizar los métodos ChildNodes o GetElementsByTagName, con lo que crearemos un objeto TDOMNodeList que debe ser liberado llegado el caso. La implementación DOM de FCL es orientada a objetos, frente a otras, como MSXML, que lo son orientadas a interfaz.

   Este ejemplo muestra cómo mostrar los nombres de los nodos en un TMemo.

   Este es el XML, en el archivo 'prueba.xml':

<xml> <?xml version="1.0"?>

<imagenes directorio="midir">
 <imagenesNodo URL="grafico.jpg" rotulo="">
   <Trozo DestinoX="0" DestinoY="0">Trozocastillo.jpg1.swf</Trozo>
   <Trozo DestinoX="0" DestinoY="86">Trozocastillo.jpg2.swf</Trozo>
 </imagenesNodo>
</imagenes></xml>

   Y aquí el código Pascal para realizar el trabajo:

<delphi> var

  Documento : TXMLDocument;
  Hijo : TDOMNode;
  j: Integer;
begin
 try
  ReadXMLFile(Documento, 'prueba.xml');
  Memo.Lines.Clear;
  // usando las propiedades FirstChild y NextSibling
  Hijo := Documento.DocumentElement.FirstChild;
  while Assigned(Hijo) do
  begin
    Memo.Lines.Add(Hijo.NodeName + ' ' + Hijo.Attributes.Item[0].NodeValue);
    // using ChildNodes method
    with Hijo.ChildNodes do
    try
      for j := 0 to (Count - 1) do
        Memo.Lines.Add(format('%s %s (%s=%s; %s=%s)',
	 [
	  Item[j].NodeName,
	  Item[j].FirstChild.NodeValue,
	  Item[j].Attributes.Item[0].NodeName, // detalles del primer atributo
	  Item[j].Attributes.Item[0].NodeValue,
	  Item[j].Attributes.Item[1].NodeName, // detalles del segundo atributo
	  Item[j].Attributes.Item[1].NodeValue
	 ]));
    finally
      Free;
    end;
    Hijo := Hijo.NextSibling;
  end;
 finally
  Documento.Free;
 end;
end;</delphi>

   El resultado en Memo1 es:

 imagenesNodo: grafico.jpg
 Trozo: Trozocastillo.jpg1.swf (DestinoX=0; DestinoY=0)
 Trozo: PTrozocastillo.jpg2.swf (DestinoX=0; DestinoY=86)

(re)Poblando un TreeView con XML

   Es habitual procesar los archivos XML para mostrar su contenido en forma de árbol. El componente TTreeView se localiza en la pestaña "Common Controls" de Lazarus.

   El código que se muestra toma un documento XML, previamente leído desde un archivo o generado por código, y con su contenido crea un árbol, en un TreeView. La etiqueta de cada nodo será el contenido del primer atributo de cada nodo XML.

<delphi> procedure TFormulario1.XML2Arbol(arbol: TTreeView; XMLDoc: TXMLDocument);

var
 iNodo: TDOMNode;
 procedure ProcesaNodo(Nodo: TDOMNode; NodoArbol: TTreeNode);
 var
   cNodo: TDOMNode;
   s: string;
 begin
   if Nodo = nil then Exit; // Parar
   
   // Añadir nodo al árbol, si tiene atributos
    
     if Nodo.HasAttributes and (Nodo.Attributes.Length>0) then
        s:=Nodo.Attributes[0].NodeValue
     else
       s:=; 
     TreeNode := tree.Items.AddChild(TreeNode, s);
   // ir al nodo hijo
   cNodo := Nodo.FirstChild;
   // Procesar todos los nodos hijos
   while cNodo <> nil do
   begin
     ProcesaNodo(cNodo, NodoArbol;
     cNodo := cNodo.NextSibling;
   end;
 end;
   
begin
 iNodo := XMLDoc.DocumentElement.FirstChild;
 while iNodo <> nil do
 begin
   ProcesaNodo(iNodo, nil); // Recursivo
   iNodo := iNodo.NextSibling;
 end;
end;</delphi>

Un ejemplo claro: <delphi> procedure XML2Tree(XMLDoc:TXMLDocument; TreeView:TTreeView);

 procedure ParseXML(Node:TDOMNode;TreeNode: TTreeNode);
 function GetNodeAttributesAsString(tNode:TDOMNode):string;
  var i:integer;
  begin
   Result:=;
   if tNode.HasAttributes then
    for i := 0 to tNode.Attributes.Length -1 do
      Result:=Result+Node.Attributes[i].NodeName+'="'+Node.Attributes[i].NodeValue+'" ';
	Result:=Trim(Result);
  end;
 begin
   if Node = nil then Exit; // Stops if reached a leaf
     //Add nodes to TreeView
     TreeNode := TreeView.Items.AddChild(TreeNode,Trim(Node.NodeName+' '+GetNodeAttributesAsString(Node)+ Node.NodeValue));
     //Continue the recursion
     Node:=Node.FirstChild;
     while Node <> Nil do
       begin
         ParseXML(Node,TreeNode);
         Node:=Node.NextSibling;
       end;
  end;
 begin
  TreeView.Items.Clear;
  ParseXML(XMLDoc.DocumentElement,nil);
 end;</delphi>
 --mdalacu 15:25, 22 August 2011 (CEST)--

Modificando un documento XML

   La primera cuestión que hay que recordar es que un TDOMDocument es un manejador del DOM. Podemos obtener una instancia, un objeto, de esta clase creando una explícitamente o bien cargando un documento XML.

   Para crear nodos XML se deben utilizar los métodos provistos por TDOMDocument y trás ello utilizar el método adecuado para ubicar el nodo en el sitio deseado en el árbol XML. Esto se debe a que un nodo debe ser propiedad de un documento concreto del DOM.

   A continuación se presentan algunos métodos comunes de TDOMDocument:

<delphi> function CreateElement(const EtiquetaNombre: DOMString): TDOMElement; virtual;

function CreateTextNode(const Datos: DOMString): TDOMText;
function CreateCDATASection(const Datos: DOMString): TDOMCDATASection; virtual;
function CreateAttribute(const nombre: DOMString): TDOMAttr; virtual;</delphi>

   CreateElement crea un nodo nuevo.

   CreateTextNode crea un valor par un nodo.

   CreateAttribute crea un atributo dentro de un nodo.

   CreateCDATA crea una sección CDATA: Los caracteres habituales de marca de XML cómo <> no se interpretan dentro de la sección CDATA. Ver artículo sobre CDATA en la Wikipedia

   Este es un ejemplo de cómo ubicar el elemento seleccionado en un TTreeView e insertar el nodo hijo que representa en el documento XML. El árbol debe ser previamente cumplimentado con el contenido del archivo XML utilizando la función XML2Tree.

<delphi> procedure TForm1.actAnyadeNodoHijo(Remitente: TObject);

var
 posicion: Integer;
 NeoNodo: TDomNode;
begin
 {*******************************************************************
 *  Hallar el elemento seleccionado
 *******************************************************************}
 if TreeView1.Selected = nil then Exit;
 if TreeView1.Selected.Level = 0 then
 begin
   posicion := TreeView1.Selected.Index;
   NeoNodo := XMLDoc.CreateElement('elemento');
   TDOMElement(NovoNo).SetAttribute('nombre', 'Elemento');
   TDOMElement(NovoNo).SetAttribute('archivo', 'Archivo');
   with XMLDoc.DocumentElement.ChildNodes do
   begin
     Item[position].AppendChild(NeoNodo);
     Free;
   end;
   {*******************************************************************
   *  Actualiza el árbol TreeView1
   *******************************************************************}
   TreeView1.Items.Clear;
   XML2Tree(TreeView1, XMLDoc);
 end
 else if TreeView1.Selected.Level >= 1 then
 begin
   {*******************************************************************
   *  Esta función únicamente trabaja en el primer nivel del árbol.
   *  pero puede ser fácilmente modificada para que lo haga en cualesquiera niveles
   *******************************************************************}
 end;
end;</delphi>

Crear un TXMLDocument desde una cadena de caracteres

   Si MiCadenaXML contiene un documento XML, el código siguiente creará su DOM:

<delphi> Var

Cadena: TStringStream;
XML : TXMLDocument;
begin
 Cadena:= TStringStream.Create(MiCadenaXML );
 Try
   Cadena.Position:=0;
   XML:=Nil;
   ReadXMLFile(XML,Cadena); // El documento XML completo
   // Alternatively:
   ReadXMLFragment(UnNodoPadre,Cadena); // Lee únicamente un fragmento del XML
 Finally
   Cadena.Free;
 end;
end;</delphi>

Validando un documento

   Desde marzo de 2007, la validación con DTD se ha añadido al analizador XML de la FCL. La validación verifica que la estructura lógica del documento se ajusta a las normas definidas en el DTD (Definición de Tipo de Documento) correspondiente.

   Este es un ejemplo de un documento XML con DTD:

<xml> <?xml version='1.0'?>

<!DOCTYPE raiz [
<!ELEMENT raiz (child)+ >
<!ELEMENT hijo (#PCDATA)>
]>
<raiz>
  <hijo>Este es el primer hijo.</hijo>
  <hijo>Y este el segundo.</hijo>
</raiz></xml>

   Este DTD especifica que el elemento 'raiz' puede tener uno o más elementos 'hijo', y que estos, los elementos 'hijo', únicamente pueden contener caracteres de datos. Si el analizador informa si detecta alguna violación de estas reglas .

   Cargar un documento de esta forma es un poco más complicado. Supongamos que tenemos datos XML en un objeto TStream:

<delphi> procedure TMiObjeto.DOMdesdeFlujo(unFlujo: TStream);

var
 Analizador : TDOMParser;
 Fuente : TXMLInputSource;
 Doc : TXMLDocument;
begin
 try
  // creamos el objeto analizador
  Analizador := TDOMParser.Create;
  // y la fuente de entrada
  Fuente := TXMLInputSource.Create(unFlujo);
  // ahora validamos
  Analizador.Options.Validate := True;
  // asignamos un manejador de errores pra recibir las notificaciones
  Analizador.OnError := @ErrorHandler;
  // realizamos el trabajo
  Analizador.Parse(Fuente, Doc);
  // ...y hacemos limpieza (general)
 finally
  Fuente.Free;
  Analizador.Free;
 end;
end;
procedure TMiObjeto.ErrorHandler(Error: EXMLReadError);
begin
 if Error.Severity = esError then  // únicamente nos interesan los errores de validación
   writeln(Error.Message);
end;</delphi>

Espacios en Blanco

   Si deseas conservar los espacios en blanco al principio de los textos de un nodo, el método anterior es la forma de cargar el documento XML. Los espacios en blanco iniciales son ignorados por defecto. Esta es la razón por la cual la función ReadXML (...) nunca devuelve los espacios en blanco en textos nodo.    Antes de llamar a Analizador.Parse(Fuente, Doc) inserta la línea

<delphi> Analizador.Options.PreserveWhitespace := True;</delphi>

   Esto obliga a que el analizador devuelva todos los espacios en blanco. ¡Esto incluye también los caracteres de nueva línea que existen en un documento XML para que sea más fácil de leer!

Creando un documento XML

   A continuación se muestra el código completo para crear un documento XML y escribirlo en un archivo.    (Esto está tomado de un tutorial del blog DeveLazarus)    Recuerda poner en la cláusula uses las unidades DOM y XMLWrite

<delphi> unit Unidad1;

{$mode objfpc}{$H+}
interface
uses
 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,
 DOM, XMLWrite;
type
 { TForm1 }
 TForm1 = class(TForm)
   Boton1: TButton;
   Etiqueta1: TLabel;
   Etiqueta2: TLabel;
   procedure Boton1Click(Originador: TObject);
 private
   { declaraciones privadas}
 public
   { declaraciones publicas }
 end;
 
var
 Form1: TForm1;
 
implementation
{ TForm1 }
procedure TForm1.Boton1Click(Originador : TObject);
var
 xdoc: TXMLDocument;                                  // variable objeto documento XML
 NodoRaiz, NodoPadre, NodoHijo: TDOMNode;                    // variables a los nodos
begin
 //crear el documento
 xdoc := TXMLDocument.create;
 NodoRaiz := xdoc.CreateElement('registrar');      //crear el nodo raíz
 Xdoc.Appendchild(NodoRaiz;                           // guardar nodo raíz
 NodoRaiz := xdoc.DocumentElement;   //crear el nodo padre
 NodoPadre := xdoc.CreateElement('usuario');
 TDOMElement(NodoPadre).SetAttribute('id', '001');       // crear los atributos del nodo padre
 NodoRaiz.Appendchild(NodoPadre);                          // guardar nodo padre
 NodoPadre := xdoc.CreateElement('nombre');                // crear el nodo hijo
 //TDOMElement(NodoPadre).SetAttribute('sexo', 'M');     // crear los atributos
 NodoHijo := xdoc.CreateTextNode('Fernando');         // insertar el valor del nodo
 NodoPadre.Appendchild(NodoHijo);                         // guardar nodo
 NodoRaiz.ChildNodes.Item[0].AppendChild(NodoPadre);       // insertar el nodo hijo en el correspondiente nodo padre
 NodoPadre := xdoc.CreateElement('edad');               // crear el nodo hijo
 //TDOMElement(NodoPadre).SetAttribute('anyo', '1976');   // crear los atributos
 NodoHijo := xdoc.CreateTextNode('32');               // insertar el valor del nodo
 NodoPadre.Appendchild(NodoHijo);                         // guardar nodo
 NodoRaiz.ChildNodes.Item[0].AppendChild(NodoPadre);       // insertar el nodo hijo en el correspondiente nodo padre
 writeXMLFile(xDoc,'prueba.xml');                     // escribir el XML
 Xdoc.free;                                          // liberar la memoria
end;
initialization
 {$I unit1.lrs}
end.</delphi>

   El resultado es el documento XML siguiente:

<xml> <?xml version="1.0"?>

<registrar>
 <usuario id="001">
   <nombre>Fernando</nombre>
   <edad>32</edad>
 </usuario>
</registrar></xml>

Codificación

   Según la norma XML puede haber un atributo de codificación en la primera línea del XML, pero no es obligatorio. Desde la versión 0.9.26 de Lazarus, existe una propiedad de codificación en TXMLDocument, pero se ignora. La función writeXMLFile utiliza siempre UTF-8 y no genera un atributo de codificación en la primera línea del documento XML.

Ver también

Enlaces externos


Tutoría de Aplicaciones Multihilo