Hay dos problemas.
El primero es que el XML está mal formado. En concreto, las etiquetas tienen un prefijo, 'cfdi', pero NO está definido como espacio de nombres dentro del XML, por lo que XML::Simple, al pasarlo al analizador de XML, se encuentra con el problema de que el analizador devuelve un error, indicando que ese prefijo no está declarado:
Using text Syntax Highlighting
Uncaught exception from user code:
Undeclared prefix: cfdi at /usr/lib/perl5/vendor_perl/5.16.0/XML/NamespaceSupport.pm line 298.
Coloreado en 0.000 segundos, usando
GeSHi 1.0.8.4
Esto se soluciona arreglando el XML, con solo incorporar un atributo a la marca raíz:
Using xml Syntax Highlighting
<cfdi:Comprobante xmlns:cfdi="http://www.w3.org/TR/html4/"
Coloreado en 0.000 segundos, usando
GeSHi 1.0.8.4
Si nosotros no somos los que generamos el XML, lo que podemos hacer es leer primero el xml como un archivo de texto normal, con una operación de sustitución (una expresión regular) añadimos ese atributo, y el resultado ya se lo podemos pasar al XMLin().
El segundo problema son los Conceptos. Muestras que quieres sacar toda la información del XML en una sola línea de texto (una línea por cada xml encontrado. ¿Eso quiere decir que todos los conceptos de cada xml deben ir también en esa misma línea? ¿Da lo mismo que haya un solo concepto o veinte?
Supongamos que eso es lo que queremos: en la línea, primero están los datos de Emisor, Receptor y Comprobante, y luego sigue en secuencia los datos de todos los conceptos.
Aquí hay un pequeño problema añadido. XML::Simple, cuando se encuentra con un XML en el que una marca se repite varias veces en el mismo nivel -caso de Concepto-, crea un
array donde cada elemento es un
hash que contiene a su vez los datos de cada marca. Pero... si solo hay UNA marca, no crea el
array.
Ejemplo: con el XML que pusiste, crea una estructura así:
Using perl Syntax Highlighting
'cfdi:Conceptos' => {
'cfdi:Concepto' => [
{
'valorUnitario' => '7843.97',
'noIdentificacion' => '000000001000169630',
'unidad' => 'PZA',
'descripcion' => 'NUEVO IPAD 32GB WIFI B',
'importe' => '7843.97',
'cantidad' => '1'
},
{
'valorUnitario' => '0.00',
...
Coloreado en 0.001 segundos, usando
GeSHi 1.0.8.4
y vemos que 'cfdi:Concepto' es una referencia a un
array, y cada elemento, un
hash con los datos del Concepto.
Pero... si solo hay UN Concepto, por defecto se genera esta estructura:
Using perl Syntax Highlighting
'cfdi:Conceptos' => {
'cfdi:Concepto' => {
'valorUnitario' => '7843.97',
'noIdentificacion' => '000000001000169630',
'unidad' => 'PZA',
'descripcion' => 'NUEVO IPAD 32GB WIFI B',
'importe' => '7843.97',
'cantidad' => '1'
}
},
Coloreado en 0.001 segundos, usando
GeSHi 1.0.8.4
No hay
array. Sigue siendo un
hash de
hash.
Estaríamos obligados, en nuestro programa, en contemplar los dos casos, pues los dos se leen de dos formas diferentes (sería poner un if(), un for() en una parte, y un acceso directo en otra).
Lo mejor es decirle a XML::Simple que SIEMPRE ponga los Concepto en forma de
array. Para eso, modificamos el XMLin() añadiendo el atributo
ForceArray => [ 'cfdi:Concepto' ], y ya sale igual que antes, aunque solo haya un elemento.
El programa queda al final así:
Using perl Syntax Highlighting
#!/usr/bin/perl
use strict;
use warnings;
#use diagnostics;
use XML::Simple;
#use Data::Dumper;
chdir 'C:/pruebas/prueba' or die "ERROR: No puedo entrar en C:/pruebas/prueba: $!\n";
opendir( DIR, '.' );
my @files = readdir(DIR);
closedir(DIR);
open( FacElec, '>>', 'C:/pruebas/FacElec.txt' );
foreach my $a (@files) {
next if $a =~ /^\./;
print "Archivo cargado: [$a]\n";
my $factura = XMLin( $a, ForceArray => ['cfdi:Concepto'], NSExpand => 0, KeepRoot => 0 );
print FacElec join q{|},
$factura->{LugarExpedicion},
$factura->{metodoDePago},
$factura->{tipoDeComprobante},
$factura->{total},
$factura->{subTotal},
$factura->{fecha},
$factura->{formaDePago},
$factura->{'cfdi:Emisor'}{nombre},
$factura->{'cfdi:Emisor'}{rfc},
$factura->{'cfdi:Emisor'}{'cfdi:DomicilioFiscal'}{codigoPostal},
$factura->{'cfdi:Emisor'}{'cfdi:DomicilioFiscal'}{pais},
$factura->{'cfdi:Emisor'}{'cfdi:DomicilioFiscal'}{estado},
$factura->{'cfdi:Emisor'}{'cfdi:DomicilioFiscal'}{municipio},
$factura->{'cfdi:Emisor'}{'cfdi:DomicilioFiscal'}{colonia},
$factura->{'cfdi:Emisor'}{'cfdi:DomicilioFiscal'}{calle},
$factura->{'cfdi:Receptor'}{nombre},
$factura->{'cfdi:Receptor'}{rfc},
$factura->{'cfdi:Receptor'}{'cfdi:Domicilio'}{codigoPostal},
$factura->{'cfdi:Receptor'}{'cfdi:Domicilio'}{pais},
$factura->{'cfdi:Receptor'}{'cfdi:Domicilio'}{estado},
$factura->{'cfdi:Receptor'}{'cfdi:Domicilio'}{municipio},
$factura->{'cfdi:Receptor'}{'cfdi:Domicilio'}{colonia},
$factura->{'cfdi:Receptor'}{'cfdi:Domicilio'}{calle},
;
for my $concepto ( @{ $factura->{'cfdi:Conceptos'}{'cfdi:Concepto'} } ) {
print FacElec '|', join q{|},
$concepto->{importe},
$concepto->{valorUnitario},
$concepto->{descripcion},
$concepto->{noIdentificacion},
$concepto->{unidad},
$concepto->{cantidad},
;
}
}
print "Fin Carga\n";
close FacElec;
Coloreado en 0.006 segundos, usando
GeSHi 1.0.8.4