Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   C++ Builder (https://www.clubdelphi.com/foros/forumdisplay.php?f=13)
-   -   Leer un fichero XML (https://www.clubdelphi.com/foros/showthread.php?t=95591)

Angel.Matilla 01-03-2022 13:47:22

Leer un fichero XML
 
Buenas. Estoy implementando en una aplicación el poder cargar un fichero XML de recibos (Órdenes en formato ISO 20022 para emisión de Adeudos Directos SEPA) que se ha creado en otra máquina para mantener la BB.DD. actualizada.
El fichero tiene esta estructura:
Código:

<?xml version="1.0" encoding="utf-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.008.001.02" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <CstmrDrctDbtInitn>
    <GrpHdr>
      <MsgId>8DA16A62A6954A25BBD465B830D0BD49</MsgId>
      <CreDtTm>2020-0730T09:56:43</CreDtTm>
      <NbOfTxs>15</NbOfTxs>
      <CtrlSum>580.00</CtrlSum>
      <InitgPty>
        <Nm>XXXXXXXXX</Nm>
        <Id>
          <OrgId>
            <Othr>
              <Id>XXXXXXXXXXXXX</Id>
            </Othr>
          </OrgId>
        </Id>
      </InitgPty>
    </GrpHdr>
    <PmtInf>
      <PmtInfId>15323007200955</PmtInfId>
      <PmtMtd>DD</PmtMtd>
      <NbOfTxs>15</NbOfTxs>
      <CtrlSum>580.00</CtrlSum>
      <PmtTpInf>
        <SvcLvl>
          <Cd>SEPA</Cd>
        </SvcLvl>
        <LclInstrm>
          <Cd>CORE</Cd>
        </LclInstrm>
        <SeqTp>RCUR</SeqTp>
      </PmtTpInf>
      <ReqdColltnDt>2020-08-02</ReqdColltnDt>
      <Cdtr>
        <Nm>XXXXXXXXXX</Nm>
      </Cdtr>
      <CdtrAcct>
        <Id>
          <IBAN>ES533190XXXXXXXXXXXXX</IBAN>
        </Id>
      </CdtrAcct>
      <CdtrAgt>
        <FinInstnId>
          <BIC>BCOEESMM190</BIC>
        </FinInstnId>
      </CdtrAgt>
      <CdtrSchmeId>
        <Id>
          <PrvtId>
            <Othr>
              <Id>XXXXXXXXXXXXXXX</Id>
              <SchmeNm>
                <Prtry>SEPA</Prtry>
              </SchmeNm>
            </Othr>
          </PrvtId>
        </Id>
      </CdtrSchmeId>
      <DrctDbtTxInf>
        <PmtId>
          <EndToEndId>1150412</EndToEndId>
        </PmtId>
        [...]
      </DrctDbtTxInf>
      [...]
    </PmtInf>
    <PmtInf>
      [...]
    </PmtInf>
  </CstmrDrctDbtInitn>
</Document>

El primer trozo, entre las etiquetas <GrpHdr> y </GrpHdr>, es único para cada fichero. Luego puede haber un número indeterminado de grupos <PmtInf> - </PmtInf>. Para leer el fichero estoy haciendo esto:
Código:

XMLDocument->Active = true;
iNode0 = XMLDocument->DocumentElement;
iNode1 = iNode0->ChildNodes->FindNode("CstmrDrctDbtInitn");
if (iNode1 == NULL)
    continue;

iNode2 = iNode1->ChildNodes->FindNode("GrpHdr");
if (iNode2 == NULL)
    continue;

iNode2 = iNode1->ChildNodes->FindNode("PmtInf");
while (iNode2 != NULL)
{
    iNode3 = iNode2->ChildNodes->FindNode("PmtInfId");
    if (iNode3 != NULL && iNode3->IsTextElement)
    {
          cRefInt = (DOMString)iNode3->Text;
          etc.
    }
    iNode2 = iNode2->NextSibling();
}

La primera vez que entra encuentar correctamente la primera etiqueta PmtInf y ejecuta todo el código que sigue pero al llegar al NextSibling, aunque haya otra etiqueta PmtInf no la encuentra y se sale del bucle. Con la ayuda de Builder había entendido que NextSibling seguía ejecutándo el bucle mientars encontrara la etiqueta correspondiente y tal vez haya interpretado mal su funcionamiento.

Angel.Matilla 01-03-2022 17:35:25

Se me había olvidado añadir que para leer los sucesivos DrctDbtTxInf, de los que hay uno por cada recibo, estoy usando este código:
Código:

iNode3 = iNode2->ChildNodes->FindNode("DrctDbtTxInf");
while (iNode3 != NULL)
{
    iNode4 = iNode3->ChildNodes->FindNode("PmtId");
    if (iNode4 != NULL)
    {
          iNode5 = iNode4->ChildNodes->FindNode("EndToEndId");
          etc.
    }
    [Código para tratar la información del recibo]
    iNode3 = iNode3->NextSibling();
}

Y funciona a la perfección; quiero decir que lee todos los recibos que hay en el fichero.

Angel.Matilla 02-03-2022 19:39:36

Bueno, después de dos días dando vueltas y buscando información encontré una solución. No sé si será la mejor, pero funciona.

Hay que definir una variable IXMLNodeList y me ha quedado así:
Código:

IXMLNodeList *lstNodos;

ThousandSeparator = ',';
DecimalSeparator  = '.';

XMLDocument->Active = true;
iNode0 = XMLDocument->DocumentElement;
iNode1 = iNode0->ChildNodes->FindNode("CstmrDrctDbtInitn");
if (iNode1 == NULL)
    continue;

iNode2 = iNode1->ChildNodes->FindNode("GrpHdr");
if (iNode2 == NULL)
    continue;

lstNodos = iNode1->ChildNodes;
iNode2 = lstNodos->FindNode((DOMString)"PmtInf");
while (iNode2 != NULL)
{
    iNode3 = iNode2->ChildNodes->FindNode("PmtInfId");
    if (iNode3 != NULL && iNode3->IsTextElement)
    {
          [Código para tratar cada nodo]
    }
    iNode2 = lstNodos->FindSibling(iNode2, 1);
}

El método FindSibling lleva dos parámetros: el primero es el nodo que se está tratando en este momento (iNode2 en este caso) y el segundo es el sentido en que se quiere avanzar en la estructura (positivo hacia adelante, negativo hacia atrás) y el número de nodos que se quiere saltar (si pone uno se busca elsiguiente igual, etc.)

Casimiro Noteví 02-03-2022 21:08:04

Gracias por compartirlo ^\||/


La franja horaria es GMT +2. Ahora son las 12:06:57.

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