¿Por qué se desaconseja XML::Simple?
De la documentación de XML::Simple
:
Se desaconseja el uso de este módulo en código nuevo. Hay otros módulos disponibles que proporcionan interfaces más sencillas y consistentes. En particular, se recomienda encarecidamente XML::LibXML.
Los principales problemas de este módulo son la gran cantidad de opciones y las formas arbitrarias en que estas opciones interactúan, a menudo con resultados inesperados.
¿Alguien puede aclararme cuáles son las razones clave de esto?
El verdadero problema es que lo que XML::Simple
principalmente intenta hacer es tomar XML y representarlo como una estructura de datos Perl.
Como sin duda sabrá por perldata
las dos estructuras de datos clave que tiene disponibles: hash
y array
.
- Las matrices son escalares ordenados.
- Los hashes son pares clave-valor desordenados.
Y XML tampoco sirve realmente. Tiene elementos los cuales son:
- no tiene un nombre único (lo que significa que los hashes no "encajan").
- .... pero están 'ordenados' dentro del archivo.
- puede tener atributos (que podrías insertar en un hash)
- puede tener contenido (pero puede que no, pero podría ser una etiqueta unaria)
- puede tener hijos (de cualquier profundidad)
Y estas cosas no se asignan directamente a las estructuras de datos de Perl disponibles (en un nivel simplista, un hash anidado de hashes podría encajar), pero no puede hacer frente a elementos con nombres duplicados. Tampoco se puede diferenciar fácilmente entre atributos y nodos secundarios.
Así que XML::Simple
intenta adivinar basándose en el contenido XML y toma 'sugerencias' de las distintas configuraciones de opciones, y luego, cuando intenta generar el contenido, (intenta) aplicar el mismo proceso a la inversa.
Como resultado, para cualquier cosa que no sea el XML más simple , se vuelve difícil de manejar en el mejor de los casos o, en el peor, pierde datos.
Considerar:
<xml>
<parent>
<child att="some_att">content</child>
</parent>
<another_node>
<another_child some_att="a value" />
<another_child different_att="different_value">more content</another_child>
</another_node>
</xml>
Esto, cuando se analiza, XML::Simple
le da:
$VAR1 = {
'parent' => {
'child' => {
'att' => 'some_att',
'content' => 'content'
}
},
'another_node' => {
'another_child' => [
{
'some_att' => 'a value'
},
{
'different_att' => 'different_value',
'content' => 'more content'
}
]
}
};
Nota: ahora tiene debajo parent
solo hashes anónimos, pero debajo another_node
tiene una serie de hashes anónimos.
Entonces, para acceder al contenido de child
:
my $child = $xml -> {parent} -> {child} -> {content};
Observe que tiene un nodo 'secundario', con un nodo de 'contenido' debajo, lo cual no se debe a que sea... contenido.
Pero para acceder al contenido debajo del primer another_child
elemento:
my $another_child = $xml -> {another_node} -> {another_child} -> [0] -> {content};
Tenga en cuenta cómo: debido a que tiene varios <another_node>
elementos, el XML se ha analizado en una matriz, donde no era uno solo. (Si tenías un elemento llamado content
debajo, entonces terminarás con algo más). Puede cambiar esto usando ForceArray
pero luego terminará con un hash de matrices de hashes de matrices de hashes de matrices, aunque al menos es consistente en el manejo de elementos secundarios. Editar: Nota, después de la discusión: este es un valor predeterminado incorrecto, en lugar de una falla con XML::Simple.
Deberías configurar:
ForceArray => 1, KeyAttr => [], ForceContent => 1
Si aplica esto al XML como se indica arriba, obtendrá:
$VAR1 = {
'another_node' => [
{
'another_child' => [
{
'some_att' => 'a value'
},
{
'different_att' => 'different_value',
'content' => 'more content'
}
]
}
],
'parent' => [
{
'child' => [
{
'att' => 'some_att',
'content' => 'content'
}
]
}
]
};
Esto le dará coherencia, porque ya no tendrá elementos de un solo nodo que se manejen de manera diferente a los de varios nodos.
Pero aún así:
- Tenga un árbol de 5 referencias de profundidad para obtener un valor.
P.ej:
print $xml -> {parent} -> [0] -> {child} -> [0] -> {content};
content
Aún tiene child
elementos hash tratados como si fueran atributos y, debido a que los hashes están desordenados, simplemente no puede reconstruir la entrada. Básicamente, debes analizarlo y luego ejecutarlo Dumper
para determinar dónde debes buscar.
Pero con una xpath
consulta, llegas a ese nodo con:
findnodes("/xml/parent/child");
Lo que no obtienes, XML::Simple
lo obtienes XML::Twig
(y supongo, XML::LibXML
pero lo sé menos bien):
xpath
apoyo.xpath
es una forma XML de expresar una ruta a un nodo. Entonces puedes 'encontrar' un nodo en lo anterior conget_xpath('//child')
. Incluso puedes usar atributos enxpath
- likeget_xpath('//another_child[@different_att]')
que seleccionarán exactamente cuál deseas. (También puedes iterar en las coincidencias).cut
ypaste
mover elementosparsefile_inplace
para permitirle modificarXML
con una edición local.pretty_print
opciones, para formatearXML
.twig_handlers
ypurge
- que le permite procesar XML realmente grande sin tener que cargarlo todo en la memoria.simplify
si realmente debes hacerlo compatible con versiones anterioresXML::Simple
.- El código es generalmente mucho más simple que intentar seguir cadenas de referencias a hashes y matrices, lo que nunca se puede hacer de manera consistente debido a las diferencias fundamentales en la estructura.
También está ampliamente disponible: es fácil de descargar CPAN
y se distribuye como un paquete instalable en muchos sistemas operativos. (Lamentablemente no es una instalación predeterminada. Todavía)
Ver: XML::Referencia rápida de Twig
A modo de comparación:
my $xml = XMLin( \*DATA, ForceArray => 1, KeyAttr => [], ForceContent => 1 );
print Dumper $xml;
print $xml ->{parent}->[0]->{child}->[0]->{content};
vs.
my $twig = XML::Twig->parse( \*DATA );
print $twig ->get_xpath( '/xml/parent/child', 0 )->text;
print $twig ->root->first_child('parent')->first_child_text('child');
XML::Simple es el analizador XML más complejo disponible
El principal problema con XML::Simple es que la estructura resultante es extremadamente difícil de navegar correctamente. $ele->{ele_name}
puede devolver cualquiera de los siguientes (incluso para elementos que siguen la misma especificación):
[ { att => 'val', ..., content => [ 'content', 'content' ] }, ... ]
[ { att => 'val', ..., content => 'content' }, ... ]
[ { att => 'val', ..., }, ... ]
[ 'content', ... ]
{ 'id' => { att => 'val', ..., content => [ 'content', 'content' ] }, ... }
{ 'id' => { att => 'val', ..., content => 'content' }, ... }
{ 'id' => { att => 'val', ... }, ... }
{ 'id' => { content => [ 'content', 'content' ] }, ... }
{ 'id' => { content => 'content' }, ... }
{ att => 'val', ..., content => [ 'content', 'content' ] }
{ att => 'val', ..., content => 'content' }
{ att => 'val', ..., }
'content'
Esto significa que tienes que realizar todo tipo de comprobaciones para ver qué tienes realmente. Pero la enorme complejidad de esto alienta a los desarrolladores a hacer suposiciones muy equivocadas. Esto lleva a que todo tipo de problemas entren en producción, lo que provoca que el código en vivo falle cuando se encuentran casos extremos.
Las opciones para hacer un árbol más regular se quedan cortas
Puede utilizar las siguientes opciones para crear un árbol más regular:
ForceArray => 1, KeyAttr => [], ForceContent => 1
Pero incluso con estas opciones, todavía se necesitan muchas comprobaciones para extraer información de un árbol. Por ejemplo, obtener los /root/eles/ele
nodos de un documento es una operación común que debería ser trivial de realizar, pero se requiere lo siguiente cuando se utiliza XML::Simple:
# Requires: ForceArray => 1, KeyAttr => [], ForceContent => 1, KeepRoot => 0
# Assumes the format doesn't allow for more than one /root/eles.
# The format wouldn't be supported if it allowed /root to have an attr named eles.
# The format wouldn't be supported if it allowed /root/eles to have an attr named ele.
my @eles;
if ($doc->{eles} && $doc->{eles}[0]{ele}) {
@eles = @{ $doc->{eles}[0]{ele} };
}
En otro analizador, se usaría lo siguiente:
my @eles = $doc->findnodes('/root/eles/ele');
XML::Simple impone numerosas limitaciones y carece de características comunes
Es completamente inútil para producir XML. Incluso con
ForceArray => 1, ForceContent => 1, KeyAttr => [], KeepRoot => 1
, hay demasiados detalles que no se pueden controlar.No conserva el orden relativo de los niños con nombres diferentes.
Tiene soporte limitado (con el backend XML::SAX) o nulo (con el backend XML::Parser) para espacios de nombres y prefijos de espacios de nombres.
Algunos servidores (por ejemplo, XML::Parser) no pueden manejar codificaciones que no estén basadas en ASCII (por ejemplo, UTF-16le).
Un elemento no puede tener un elemento hijo y un atributo con el mismo nombre.
No puede crear documentos XML con comentarios.
Haciendo caso omiso de los principales problemas mencionados anteriormente, XML::Simple aún podría utilizarse con estas limitaciones. Pero ¿por qué tomarse la molestia de comprobar si XML::Simple puede manejar el formato de su documento y correr el riesgo de tener que cambiar a otro analizador más adelante? Simplemente podría utilizar un mejor analizador para todos sus documentos desde el principio.
Otros analizadores no sólo no lo someten a estas limitaciones, sino que además proporcionan muchas otras características útiles. Las siguientes son algunas características que podrían tener y que XML::Simple no tiene:
Velocidad. XML::Simple es extremadamente lento, especialmente si utiliza un backend que no sea XML::Parser. Estoy hablando de órdenes de magnitud más lentas que otros analizadores.
Selectores XPath o similares.
Soporte para documentos extremadamente grandes.
Soporte para impresión bonita.
¿XML::Simple alguna vez es útil?
El único formato para el cual XML::Simple es más simple es aquel en el que ningún elemento es opcional. He tenido experiencia con innumerables formatos XML y nunca me había encontrado con un formato así.
Esta fragilidad y complejidad por sí solas son razones suficientes para justificar mantenerse alejado de XML::Simple, pero hay otras.
Alternativas
Yo uso XML::LibXML. Es un analizador extremadamente rápido y con todas las funciones. Si alguna vez necesitara manejar documentos que no caben en la memoria, usaría XML::LibXML::Reader (y su copyCurrentNode(1)
) o XML::Twig (usando twig_roots
).
No estoy de acuerdo con los documentos.
No estaré de acuerdo y diré que eso XML::Simple
es así de simple. Y siempre me ha resultado fácil y divertido utilizarlo. Pruébelo con la información que está recibiendo. Mientras la entrada no cambie, estás bien. Las mismas personas que se quejan del uso XML::Simple
se quejan del uso JSON::Syck
para serializar Moose. Los documentos están equivocados porque tienen en cuenta la corrección sobre la eficiencia. Si solo te importa lo siguiente, estás bien:
- no tirar datos
- construir según un formato proporcionado y no un esquema abstracto
Si está creando un analizador abstracto que no está definido por aplicación sino por especificación, usaría algo más. Una vez trabajé en una empresa y tuvimos que aceptar 300 esquemas XML diferentes, ninguno de los cuales tenía una especificación. XML::Simple
Hizo el trabajo fácilmente. Las otras opciones nos habrían requerido contratar a alguien para realizar el trabajo. Todo el mundo piensa que XML es algo que se envía en un formato rígido que lo abarca todo, de modo que si escribe un analizador, está bien. Si ese es el caso, no lo use XML::Simple
. XML, antes de JSON, era simplemente un formato de "deshacerse de esto y caminar" de un idioma a otro. La gente realmente usaba cosas como XML::Dumper
. En realidad, nadie sabía lo que se produjo. ¡ Lidiar con ese escenario XML::Simple
es genial! Las personas sensatas todavía utilizan JSON sin especificaciones para lograr lo mismo. Así es como funciona el mundo.
¿Quiere leer los datos y no preocuparse por el formato? ¿Quiere recorrer estructuras Perl y no posibilidades XML? Ir XML::Simple
.
Por extensión...
Del mismo modo, para la mayoría de las aplicaciones JSON::Syck
es suficiente deshacerse de esto y caminar. Aunque si estás enviando a mucha gente, te recomiendo encarecidamente que no seas un idiota y hagas una especificación a la que exportes. Pero, ¿sabes qué? En algún momento recibirás una llamada de alguien con quien no quieres hablar y que quiere sus datos que normalmente no exportas. Y vas a pasarlo por JSON::Syck
el vudú y dejar que se preocupen por ello. ¿Si quieren XML? Cárgales $500 más y enciéndete XML::Dumper
.
Llevar
Puede que no sea perfecto, pero XML::Simple
es muy eficiente. Cada hora ahorrada en este campo, potencialmente podrás gastarla en un campo más útil. Esa es una consideración del mundo real.
las otras respuestas
Mira XPath tiene algunas ventajas. Cada respuesta aquí se reduce a preferir XPath a Perl. Está bien. Si prefiere utilizar un lenguaje específico de dominio XML estandarizado para acceder a su XML, ¡hágalo!
Perl no proporciona un mecanismo sencillo para acceder a estructuras opcionales profundamente anidadas.
var $xml = [ { foo => 1 } ]; ## Always w/ ForceArray.
var $xml = { foo => 1 };
Obtener el valor de foo
aquí en estos dos contextos puede resultar complicado. XML::Simple
sabe esto y es por eso que puedes forzar lo primero. Sin embargo, incluso con ForceArray
, si el elemento no está allí, arrojarás un error.
var $xml = { bar => [ { foo => 1 } ] };
ahora, si bar
es opcional, deberá acceder a él $xml->{bar}[0]{foo}
y @{$xml->{bar}}[0]
arrojará un error. De todos modos, eso es sólo Perl. Esto tiene 0 que ver con XML::Simple
mi humilde opinión. Y admití que eso XML::Simple
no es bueno para construir según las especificaciones. Muéstrame datos y podré acceder a ellos con XML::Simple.