¿Por qué se desaconseja XML::Simple?

Resuelto Sobrique asked hace 8 años • 3 respuestas

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?

Sobrique avatar Oct 22 '15 02:10 Sobrique
Aceptado

El verdadero problema es que lo que XML::Simpleprincipalmente intenta hacer es tomar XML y representarlo como una estructura de datos Perl.

Como sin duda sabrá por perldatalas dos estructuras de datos clave que tiene disponibles: hashy 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::Simpleintenta 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::Simplele 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 parentsolo hashes anónimos, pero debajo another_nodetiene 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_childelemento:

 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 contentdebajo, entonces terminarás con algo más). Puede cambiar esto usando ForceArraypero 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};

contentAún tiene childelementos 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 Dumperpara determinar dónde debes buscar.

Pero con una xpathconsulta, llegas a ese nodo con:

findnodes("/xml/parent/child"); 

Lo que no obtienes, XML::Simplelo obtienes XML::Twig(y supongo, XML::LibXMLpero lo sé menos bien):

  • xpathapoyo. xpathes una forma XML de expresar una ruta a un nodo. Entonces puedes 'encontrar' un nodo en lo anterior con get_xpath('//child'). Incluso puedes usar atributos en xpath- like get_xpath('//another_child[@different_att]')que seleccionarán exactamente cuál deseas. (También puedes iterar en las coincidencias).
  • cuty pastemover elementos
  • parsefile_inplacepara permitirle modificar XMLcon una edición local.
  • pretty_printopciones, para formatear XML.
  • twig_handlersy purge- que le permite procesar XML realmente grande sin tener que cargarlo todo en la memoria.
  • simplifysi realmente debes hacerlo compatible con versiones anteriores XML::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 CPANy 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');
Sobrique avatar Oct 21 '2015 19:10 Sobrique

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/elenodos 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).

ikegami avatar Oct 22 '2015 04:10 ikegami

No estoy de acuerdo con los documentos.

No estaré de acuerdo y diré que eso XML::Simplees 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::Simplese quejan del uso JSON::Syckpara 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::SimpleHizo 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::Simplees 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::Syckes 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::Syckel 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::Simplees 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 fooaquí en estos dos contextos puede resultar complicado. XML::Simplesabe 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 bares 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::Simplemi humilde opinión. Y admití que eso XML::Simpleno es bueno para construir según las especificaciones. Muéstrame datos y podré acceder a ellos con XML::Simple.

Evan Carroll avatar Oct 22 '2015 16:10 Evan Carroll