PDA

Ver la Versión Completa : ghXMLDoc XML rebelde


fjcg02
16-12-2013, 23:24:38
Buenas noches,
aquí ando de nuevo para que mi viejito Delphi me saque de algún apuro...

El tema es que necesito leer un XML como este que os pongo.

El caso es que los ejemplos que he visto siempre, en el xml vienen registros con los campos tal que así
<Datos>
<Campo1>22222</Campo1>
<Campo2>333333333</Campo2>
<Campo3>JUAN XXXXXX</Campo3>
<Campo4>CLIENTE</Campo4>
</Datos>

Con los excelentes ejemplos de algunos foreros, no hay problema en pasarlos a un dataset o similar para manejarlos.

Ahora me encuentro esto:

<?xml version="1.0"?>
<UDSObjectList>
<UDSObject>
<Handle>nr:9919C86B8B3A2B44BF43B68ED759A613</Handle>
<Attributes>
<Attribute DataType="2002">
<AttrName>persistent_id</AttrName>
<AttrValue>nr:9919C86B8B3A2B44BF43B68ED759A613</AttrValue>
</Attribute>
<Attribute DataType="2002">
<AttrName>name</AttrName>
<AttrValue>733</AttrValue>
</Attribute>
<Attribute DataType="2005">
<AttrName>resource_subclass</AttrName>
<AttrValue>1000021</AttrValue>
</Attribute>
</Attributes>
</UDSObject>
<UDSObject>
<Handle>nr:A8F861F0737D2C4181901C775E192751</Handle>
<Attributes>
<Attribute DataType="2002">
<AttrName>persistent_id</AttrName>
<AttrValue>nr:A8F861F0737D2C4181901C775E192751</AttrValue>
</Attribute>
<Attribute DataType="2002">
<AttrName>name</AttrName>
<AttrValue>734</AttrValue>
</Attribute>
<Attribute DataType="2005">
<AttrName>resource_subclass</AttrName>
<AttrValue>1000021</AttrValue>
</Attribute>
</Attributes>
</UDSObject>
</UDSObjectList>


No hay pelotas de entrale al jodido, ya me echa humo la cebolla.

Agradecería algún empujoncito de algún alma caritativa.

Un saludo y gracias de antemano.

Ñuño Martínez
17-12-2013, 18:04:08
¿Cuál es exactamente el problema? ¿Se produce algún error? ¿O los datos que obtienes no son los esperados?

Al González
17-12-2013, 19:40:52
Hola Javier. :)

La estructura de ese archivo es bastante sencilla, y combinando TghXMLDoc con XPath (http://msdn.microsoft.com/en-us/library/ms256086.aspx) es muy fácil leer los datos.

Preparé este ejemplo pensando en tu caso:
uses
Forms, DB, DBClient, Controls, Grids, DBGrids, StdCtrls, Classes,
GHFXMLDoc;

type
TForm1 = class(TForm)
mm1: TMemo;
mm2: TMemo;
Label1: TLabel;
Button1: TButton;
gr1: TDBGrid;
ds1: TDataSource;
dt1: TClientDataSet;
dt1ID: TStringField;
dt1Nombre: TStringField;
dt1Subclase: TStringField;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
Doc :TghXMLDoc; // Variable para manejar el documento XML
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

Uses
GHFRTL;

procedure TForm1.FormCreate(Sender: TObject);
begin
{ Abrimos el archivo Objetos.xml que se encuentra en el mismo directorio
que este programa (la función ghDirPath devuelve una ruta de archivo
con base en la ruta del programa ejecutable) }
Doc := TghXMLDoc.Create (ghDirPath ('Objetos.xml'));

{ Mostramos su contenido dentro del cuadro de texto mm1 (el método
FormatContent devuelve el contenido XML pero con saltos de línea y
sangrado para mejor legibilidad) }
mm1.Text := Doc.FormatContent;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
Doc.Free; // Cerramos el documento XML y destruimos el objeto Doc
end;

procedure TForm1.Button1Click(Sender: TObject);
Var
Atributos, Objetos :OLEVariant;
I, J :Integer;
Valor :String;
begin
{ Del archivo XML, obtenemos una lista de todos los elementos de nombre
"UDSObject" (registros objeto) }
Objetos := Doc.Nodes ('//UDSObject');

mm2.Clear;
mm2.Lines.Add ('Número de objetos: ');
mm2.Lines.Add (Objetos.Length);
mm2.Lines.Add ('');
mm2.Lines.Add ('Identificadores de cada objeto: ');

// Desplegamos el valor del elemento Handle de cada elemento UDSObject
For I := 0 To Objetos.Length - 1 Do
mm2.Lines.Add (Objetos.Item (I).SelectSingleNode ('Handle').Text);

mm2.SetFocus;
mm2.SelStart := 0;
mm2.SelLength := 0;
dt1.EmptyDataSet;

// Llenamos un conjunto de datos con los registros XML de objetos
For I := 0 To Objetos.Length - 1 Do
Begin
dt1.Append;

{ Obtenemos la lista de atributos del objeto I (todos los elementos
"Attribute" hijos del elemento "Attributes" del objeto en turno) }
Atributos := Objetos.Item (I).SelectNodes ('Attributes/Attribute');

For J := 0 To Atributos.Length - 1 Do
Begin
// Valor del atributo/campo
Valor := Atributos.Item (J).SelectSingleNode ('AttrValue').Text;

// Asignación según sea el nombre del atributo
If Atributos.Item (J).SelectSingleNode ('AttrName').Text =
'persistent_id' Then
dt1.FieldByName ('ID').Value := Valor
Else
If Atributos.Item (J).SelectSingleNode ('AttrName').Text =
'name' Then
dt1.FieldByName ('Nombre').Value := Valor
Else
If Atributos.Item (J).SelectSingleNode ('AttrName').Text =
'resource_subclass' Then
dt1.FieldByName ('Subclase').Value := Valor;
End;
End;

dt1.First;
end;
Espero te sirva, mi amigo. O ya me dices qué le ajustamos.

Un saludo.

fjcg02
17-12-2013, 19:54:52
Hombre, amigo Al, gracias que estás ahí...

Todavía no lo he probado, pero seguro que funciona.

El problema es que no controlo xPath, y cuando intento leer ( no sé si nodos, o qué demonios ) me dice que no ha encontrado nada y que no se puede escribir un nulo ( esto al intentar sacarlo en el memo).

Otra opción que tenía y que he estado mirando es utilizar el xlmmapper, pero prefería no hacerlo para no tener que distribuir el fichero.

En breve lo pruebo y os indico el resultado.

Muchísimas gracias

Al González
17-12-2013, 20:23:23
El problema es que no controlo xPath [...]
Como veterano que eres, seguramente recordarás los tiempos en que se usaba "CD ..\Data", "Copy \Info\X.txt ." y comandos similares para tratar con el sistema de archivos y directorios.

Bueno, pues XPath es una cosa muy parecida pero en lugar de directorios y archivos contenidos en un disco, trata con nodos (elementos y atributos) contenidos de un archivo XML. Hace tiempo encontré este excelente manual (http://www.w3schools.com/xpath/default.asp) sobre XPath, lo recomiendo a todos ampliamente, además del enlace del mensaje anterior. No siento dominarlo aún, pero practicando se avanza en ello.

El ejemplo está probado, espero tus comentarios por si hace falta algo más. :)

fjcg02
18-12-2013, 16:36:08
El ejemplo está probado, espero tus comentarios por si hace falta algo más. :)

El único comentario a realizar es darte de nuevo las gracias. Funciona perfectamente.

Un "Al-brazo"

Al González
18-12-2013, 18:28:37
Me alegra saberlo. Siendo así, me dispongo a subir el código de este ejemplo al repositorio (http://terawiki.clubdelphi.com/Delphi/Componentes-Funciones/__GH_Freebrary__/). :)

Conviene recordar a todos que estoy receptivo a ideas sobre cómo mejorar o ampliar este componente. Enlace (http://www.clubdelphi.com/foros/showthread.php?t=84523).