Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Lazarus, FreePascal, Kylix, etc. (https://www.clubdelphi.com/foros/forumdisplay.php?f=14)
-   -   Error leyendo archivo XML (https://www.clubdelphi.com/foros/showthread.php?t=88186)

arturom 24-04-2015 10:32:57

Error leyendo archivo XML
 
Buenos días,
me han encargado un pequeño proyecto en el que tengo que descargar un archivo XML de internet (que es, básicamente, la extracción de una base de datos) para extraer una serie de datos (no todos) del mismo y crear un archivo de texto plano con ellos.

Estoy trabajando con Lazarus, sobre linux.

Como nunca había trabajado con archivos XML he tenido que buscar información por internet. He encontrado un pequeño ejemplo aquí que funciona bien.

El problema es que el archivo XML no es consistente en la cantidad de campos que almacena de cada registro. Dicho de otra manera, hay registros que almacenan 32 campos y otros 33, 34, 35...

En principio no debería ser ningún problema dado que uso un bucle que recorre todos los nodos hijo:
Código:

for j := 0 to (Item[i].ChildNodes.Count - 1) do begin
pero por alguna circunstancia que no comprendo el programa me da un error "External:SIGSEGV" al llegar al primer registro que no tiene 32 campos.

Casimiro Notevi 24-04-2015 10:48:09

Sería más fácil si pones tu código y, a ser posible, un ficherito de pruebas.

duilioisola 24-04-2015 11:56:45

Sobre todo verifica si la variable "i" tiene el valor correcto.
Puede que estés pidiendo la cantidad de nodos del registro anterior, si no has incrementeado todavía esta variable.

Como te dice Casimiro: Pon un poco más de código. Sobre todo las partes en donde haces el bucle sobre "i" y dónde la incrementas.

arturom 24-04-2015 14:04:57

1 Archivos Adjunto(s)
Cita:

Empezado por duilioisola (Mensaje 491673)
Sobre todo verifica si la variable "i" tiene el valor correcto.
Puede que estés pidiendo la cantidad de nodos del registro anterior, si no has incrementeado todavía esta variable.

Como te dice Casimiro: Pon un poco más de código. Sobre todo las partes en donde haces el bucle sobre "i" y dónde la incrementas.

Gracias por vuestra respuesta.

Este el procedimiento que hace la lectura del archivo. Los datos que se obtienen se pasan a un componente MEMO (recordad que sólo estoy haciendo pruebas)

El error salta al llegar a la línea
Código:

Memo.Lines.Add(IntToStr(i) + '.' + IntToStr(j) + ' ' + Item[j].NodeName + ' -> ' + Item[j].FirstChild.NodeValue);
cuando llega al registro 9 (que tiene 33 campos, uno más que los 8 anteriores).

Como información, si sólo dejo 8 registros en el archivo XML el programa no falla.

Código Delphi [-]
procedure TFLMXL.btnLeerClick ( Sender:TObject ) ;
var
  //variable de tipo TXMLDocument para cargar documentos XML
  Doc :  TXMLDocument;
  i, j : integer;
begin
  Doc := TXMLDocument.Create;
  //cargando el archivo XML
  ReadXMLFile(Doc, 'ListaArticulos.xml');
  Memo.Lines.Clear;
  //obteniendo el nombre del nodo raiz
  Memo.Lines.Add('Archvo tipo : '+Doc.DocumentElement.NodeName);
  //trabajando con los nodos inferiores a la raiz
  with Doc.DocumentElement.ChildNodes do begin
    {la variable "Count", propiedad de ChildNodes
     contiene el numero total de nodos hijos que existen
     en este caso en el nodo raiz}
     for i := 0 to (Count - 1) do begin
      {Item[i] es el nodo al que vamos a acceder y su propiedad
       NodeName nos devuelve el nombre del nodo}
      Memo.Lines.Add(intToStr(i)+'. '+Item[i].NodeName);
      {ahora usamos la propiedad Count pero ahora de los nodos
       con los que estamos trabajando}
       for j := 0 to (Item[i].ChildNodes.Count - 1) do begin
        {ahora entraremos a los nodos hijos de los nodos
         anteriores}
        with Item[i].ChildNodes do begin
          {Item[j].FirstChild nos devuelve el primer nodo hijo y
           NodeValue es la propiedad que nos devolvera el valor
           o contenido de ese nodo}
          Memo.Lines.Add(IntToStr(i)+'.'+IntToStr(j)+' '
            + Item[j].NodeName + ' -> '
            + Item[j].FirstChild.NodeValue);
          if Item[j].NodeName = 'val' then
            Memo.Lines.Add('  Atrbuto "tipo" del nodo val = '
            + Item[j].Attributes.GetNamedItem('tipo').NodeValue);
              {Para obtener los atributos de un nodo utilizamos la propiedad Attributes}
        end;
      end;
    end;
  end;
  Doc.Free;
  //es necesario liberar la memoria que ocupo la variable Doc
end;

Adjunto el fichero que tengo para hacer las pruebas (es una parte del original con solamente 10 registros).

Pd. He compilado en 32 y 64 y el resultado es el mismo.
Ppd. Las unidades que usa el programa son DOM y XMLRead

nlsgarcia 25-04-2015 00:00:24

arturom,

Cita:

Empezado por arturom
...tengo que descargar un archivo XML de internet...extraer una serie de datos...y crear un archivo de texto plano...El problema es que el archivo XML no es consistente en la cantidad de campos que almacena de cada registro...

:rolleyes:

Revisa este código:
Código Delphi [-]
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, xmldom, XMLIntf, msxmldom, XMLDoc, Math;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    ListBox1: TListBox;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

const
   FileNameXML = 'File_FileName.xml';
   FileNameTXT = 'File_FileName.txt';
   FieldFrom = 1; // Mínima cantidad de campos del archivo XML
   FieldTo = 5;   // Máxima cantidad de campos del archivo XML

var
  Form1: TForm1;

implementation

{$R *.dfm}

// Crea un archivo XML
procedure TForm1.Button1Click(Sender: TObject);
var
   XML : TXMLDocument;
   ChildNode : IXMLNode;
   i, j : Integer;

begin

   Randomize;

   XML := TXMLDocument.Create(nil);

   SetCurrentDir(ExtractFilePath(Application.ExeName));

   try

      XML.Active := true;

      // Crea el cuerpo princial del documento XML
      XML.AddChild('Document_File_XML.xml');

      for i := 1 to 3 do
      begin
         // Crea el Nodo Raíz o Registro
         ChildNode := XML.DocumentElement.AddChild('Root_Node-' + IntToStr(i));
         for j:= 1 to RandomRange(FieldFrom,FieldTo+1) do  // Genera N Nodos Hijos o Campos del Archivo XML de forma Aleatoria
            // Crea el Nodo Hijo o Campo del Registro
            ChildNode.AddChild('Child_Node-' + IntToStr(j)).text := 'Node-' + IntToStr(i) + ' TextChild-' + IntToStr(j);
      end;

      // Identa el Documento XML
      XML.XML.Text := xmlDoc.FormatXMLData(XML.XML.Text);

      XML.Active := true;

      // Salva el archivo creado
      XML.SaveToFile(FileNameXML);

      XML.Active := False;

   finally

      XML := nil;

   end;

end;

// Lee un archivo XML
procedure TForm1.Button2Click(Sender: TObject);
var
   XML : TXMLDocument;
   RootNode : IXMLNode;
   i,j : Integer;

begin

   ListBox1.Clear;

   SetCurrentDir(ExtractFilePath(Application.ExeName));

   XML := TXMLDocument.Create(Self);

   try

      XML.Active := True;

      // Carga el archivo XML creado
      XML.LoadFromFile(FileNameXML);

      // Lee todos los Nodos o Registros
      ListBox1.Items.Add(StringOfChar('-',10));

      for i:= 0 to XML.DocumentElement.ChildNodes.Count - 1 do
      begin
         // Lee un Nodo o Registro
         RootNode := XML.DocumentElement.ChildNodes[i];
         for j:= 0 to RootNode.ChildNodes.Count - 1 do
            // Lee cada Nodo Hijo o Campo del registro
            ListBox1.Items.Add(RootNode.ChildNodes.Nodes[j].Text);
         ListBox1.Items.Add(StringOfChar('-',10));
      end;

      XML.Active := False;

   finally

      XML := nil;

   end;

end;

// Crea un archivo TXT en base a un archivo XML
procedure TForm1.Button3Click(Sender: TObject);
var
   XML : TXMLDocument;
   RootNode : IXMLNode;
   i,j : Integer;
   F : TextFile;
   Buffer : String;

begin

   SetCurrentDir(ExtractFilePath(Application.ExeName));

   XML := TXMLDocument.Create(Self);

   try

      FileMode := fmOpenWrite;
      AssignFile(F, FileNameTXT);
      Rewrite(F);

      XML.Active := True;

      // Carga el archivo XML creado
      XML.LoadFromFile(FileNameXML);

      // Lee todos los Nodos o Registros
      for i:= 0 to XML.DocumentElement.ChildNodes.Count - 1 do
      begin

         // Lee un Nodo o Registro
         RootNode := XML.DocumentElement.ChildNodes[i];

         // Genera el archivo txt en base a cada Nodo Hijo o Campo del registro xml
         Buffer := EmptyStr;

         for j:= 0 to RootNode.ChildNodes.Count - 1 do
            Buffer := Buffer + RootNode.ChildNodes.Nodes[j].Text + ',';

         Buffer := Copy(Buffer,1,Length(Buffer)-1);

         Writeln(F,Buffer);

      end;

      XML.Active := False;

   finally

      XML := nil;
      CloseFile(F);

   end;

end;

end.
El código anterior en Delphi 7 sobre Windows 7 Professional x32, Crea un archivo XML con registros de diferente cantidad de campos, Muestra el mismo en un TListBox y Genera un archivo TXT en base al XML, como se puede ver en la siguiente imagen:



Espero sea útil :)

Nelson.

arturom 27-04-2015 09:40:18

Cita:

Empezado por nlsgarcia (Mensaje 491702)
arturom,


:rolleyes:

Revisa este código:

...

El código anterior en Delphi 7 sobre Windows 7 Professional x32, Crea un archivo XML con registros de diferente cantidad de campos, Muestra el mismo en un TListBox y Genera un archivo TXT en base al XML, como se puede ver en la siguiente imagen:

...

Espero sea útil :)

Nelson.

Gracias Nelson,
voy a revisarlo, aunque ya comenté que trabajo en Lazarus sobre linux.
Voy a intentar adaptarlo y hacer pruebas con el.

Saludos

arturom 27-04-2015 13:48:34

Hola de nuevo,
poco a poco voy avanzando en el problema.

Aunque agradezco a Nelson su interés y dedicación, el programa que estoy haciendo realmente funciona bien.
Después de hacer varias comprobaciones, he detectado que el bucle funciona como debería.

El problema surge porque uno de los registros del archivo viene con un campo que no contiene ningún valor, así, al llegar a esta línea
Código Delphi [-]
if Item[j].NodeName = 'ean' then
  t_EAN:=Item[j].FirstChild.NodeValue;
me da el error SIGSEGV.

Intento solucionarlo añadiendo una cláusula if...
Código Delphi [-]
if Item[j].NodeName = 'ean' then
  if Item[j].FirstChild.NodeValue = '' then
    t_EAN := 'XXXXXXXXXXXXX';
pero sigue saltando el error SIGSEGV al comparar NodeValue con una cadena vacía.

No lo entiendo, se supone que el valor de NodeValue es un DOMString->XMLString->WideString con lo que no debería saltar el error...

PD. He detectado que en realidad, el error salta en la línea
Código Delphi [-]
if Item[j].NodeName = 'ean' then
sin ni siquiera entrar al segundo if...

duilioisola 27-04-2015 13:59:50

Deberías entonces mirar primero si contiene un valor.

Código Delphi [-]
if Item[j].NodeName = 'ean' then
begin
  // Primero verifico si hay datos
  if (Item[j].FirstChild.HasChildNode) then
    t_EAN := Item[j].FirstChild.NodeValue
  else
    t_EAN := 'XXXXXXXXXXXXX';
end;

nlsgarcia 28-04-2015 05:56:31

arturom,

Cita:

Empezado por arturom
...trabajo en Lazarus sobre Linux...tengo que descargar un archivo XML de internet...extraer una serie de datos...y crear un archivo de texto plano...El problema es que el archivo XML no es consistente en la cantidad de campos que almacena de cada registro...

:rolleyes:

Revisa este código:
Código Delphi [-]
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, DOM, XMLRead, XMLWrite, Math;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    ListBox1: TListBox;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

const
  FileNameXML = 'File_FileName.xml';
  FileNameTXT = 'File_FileName.txt';
  FieldFrom = 1; // Mínima cantidad de campos del archivo XML
  FieldTo = 5;   // Máxima cantidad de campos del archivo XML

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

// Crea un archivo XML
procedure TForm1.Button1Click(Sender: TObject);
var
   Doc : TXMLDocument;
   RootNode, ElementNode,ItemNode,TextNode : TDOMNode;
   i, j : integer;

begin

  try

     SetCurrentDir(ExtractFilePath(Application.ExeName));

     // Crea el cuerpo princial del documento XML
     Doc := TXMLDocument.Create;

     // Crea el Nodo Raiz
     RootNode := Doc.CreateElement('File');
     Doc.Appendchild(RootNode);
     RootNode := Doc.DocumentElement;

     // Crea los Nodos de la Raiz
     for i := 1 to 3 do
     begin
        // Crea un Nodo Registro
        ElementNode := Doc.CreateElement('Record-' + IntToStr(i));

        for j := 1 to RandomRange(FieldFrom,FieldTo+1) do  // Genera N Nodos Hijos o Campos del Archivo XML de forma Aleatoria
        begin
           // Crea un Nodo Hijo o Campo del Registro
           ItemNode := Doc.CreateElement('Field-' + IntToStr(j));
           TextNode := Doc.CreateTextNode('Field-' + IntToStr(j) + ' Value-' + IntToStr(Random(100)));
           ItemNode.AppendChild(TextNode);
           ElementNode.AppendChild(ItemNode);
        end;

        RootNode.AppendChild(ElementNode);
    end;

    // Salva el archivo XML
    WriteXMLFile(Doc,FileNameXML);

  finally

    Doc.Free;

  end;

end;

// Lee un archivo XML
procedure TForm1.Button2Click(Sender: TObject);
var
   Doc : TXMLDocument;
   RootNode, ElementNode,ItemNode : TDOMNode;
   i, j, k : integer;

begin

   try

      ListBox1.Clear;

      SetCurrentDir(ExtractFilePath(Application.ExeName));

      // Crea un Documento XML
      Doc := TXMLDocument.Create;

      // Lee el Archivo XML
      ReadXMLFile(Doc, FileNameXML);

      RootNode := Doc.DocumentElement;

      ListBox1.Items.Add(StringOfChar('-',10));

      // Recorre estructura dek archivo XML
      for i := 0 to RootNode.ChildNodes.Count - 1 do
      begin

         ElementNode := RootNode.ChildNodes[i];
         ItemNode := ElementNode;

         // Lee cada Nodo Hijo o Campo del registro
         for j := 0 to ElementNode.ChildNodes.Count - 1 do
            for k := 0 to ItemNode.ChildNodes.Item[j].ChildNodes.Count - 1 do
               ListBox1.Items.Add(ItemNode.ChildNodes.Item[j].ChildNodes.Item[k].NodeValue);

         ListBox1.Items.Add(StringOfChar('-',10));

      end;

   finally

      Doc.Free;

   end;

end;

// Crea un archivo TXT en base a un archivo XML
procedure TForm1.Button3Click(Sender: TObject);
var
   Doc : TXMLDocument;
   RootNode, ElementNode, ItemNode : TDOMNode;
   i, j, k : integer;
   F : TextFile;
   Buffer : String;

begin

   try

      SetCurrentDir(ExtractFilePath(Application.ExeName));

      // Crea archivo TXT
      FileMode := fmOpenWrite;
      AssignFile(F, FileNameTXT);
      Rewrite(F);

      // Crea un Documento XML
      Doc := TXMLDocument.Create;

      // Lee el Archivo XML
      ReadXMLFile(Doc, FileNameXML);

      RootNode := Doc.DocumentElement;

      // Recorre la estructura del archivo XML
      for i := 0 to RootNode.ChildNodes.Count - 1 do
      begin

         ElementNode := RootNode.ChildNodes[i];
         ItemNode := ElementNode;

         // Genera el archivo txt en base a cada Nodo Hijo o Campo del registro xml

         Buffer := EmptyStr;

         for j := 0 to ElementNode.ChildNodes.Count - 1 do
            for k := 0 to ItemNode.ChildNodes.Item[j].ChildNodes.Count - 1 do
               Buffer := Buffer + ItemNode.ChildNodes.Item[j].ChildNodes.Item[k].NodeValue + ',';

         Buffer := Copy(Buffer,1,Length(Buffer)-1);

         Writeln(F,Buffer);

      end;

   finally

      Doc.Free;
      CloseFile(F);

   end;

end;

end.
El código anterior en Lazarus 1.4.0 FPC 2.6.4 sobre Windows 7 Professional x32, Crea un archivo XML con registros de diferente cantidad de campos, Muestra el mismo en un TListBox y Genera un archivo TXT en base al XML, como se puede ver en la siguiente imagen:



Revisa esta información:
Cita:

XML Tutorial
Espero sea útil :)

Nelson.

arturom 28-04-2015 08:19:41

Cita:

Empezado por duilioisola (Mensaje 491732)
Deberías entonces mirar primero si contiene un valor.

Código Delphi [-]
if Item[j].NodeName = 'ean' then
begin
  // Primero verifico si hay datos
  if (Item[j].FirstChild.HasChildNode) then
    t_EAN := Item[j].FirstChild.NodeValue
  else
    t_EAN := 'XXXXXXXXXXXXX';
end;

Gracias por tu interés duilioisola, pero sigue fallando.

Tal y como comentaba en mi último mensaje, se detiene con un error SIGSEGV en la línea if (Item[j].FirstChild.HasChildNode) then por lo que no hace asignaciones.

arturom 28-04-2015 08:39:23

Error leyendo archivo XML (SOLUCIONADO)
 
Cita:

Empezado por nlsgarcia (Mensaje 491751)
arturom,


:rolleyes:

Revisa este código:
Código Delphi [-]
unit Unit1;

{$mode objfpc}{$H+}

interface

...

end.
El código anterior en Lazarus 1.4.0 FPC 2.6.4 sobre Windows 7 Professional x32, Crea un archivo XML con registros de diferente cantidad de campos, Muestra el mismo en un TListBox y Genera un archivo TXT en base al XML, como se puede ver en la siguiente imagen:

...

Espero sea útil :)

Nelson.

Vaya Nelson, es ¡¡ FANTÁSTICO !! v:-)v

No se me ocurre ninguna manera de agradecértelo lo suficiente, ya estaba empezando a pensar que no había forma de controlarlo.

Funciona perfecto, el primer procedimiento no lo necesito, ya que obtengo el archivo XML directamente de internet, pero aún así, de verdad, muchas gracias por tu interés y esfuerzo.

Apúntate que te debo unas cuantas ||-||


La franja horaria es GMT +2. Ahora son las 03:07:23.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi