Почему XML::Simple не рекомендуется?
Из документации XML::Simple
:
Использование этого модуля в новом коде не рекомендуется. Доступны другие модули, которые обеспечивают более простые и согласованные интерфейсы. В частности, настоятельно рекомендуется XML::LibXML.
Основными проблемами этого модуля являются большое количество опций и произвольные способы взаимодействия этих опций - часто с неожиданными результатами.
Может кто-нибудь уточнить для меня, каковы основные причины этого?
3 ответа
Настоящая проблема в том что XML::Simple
в первую очередь пытается взять XML и представить его как структуру данных perl.
Как вы, несомненно, будете знать из perldata
две ключевые структуры данных, которые у вас есть, это hash
и array
,
- Массивы - это упорядоченные скаляры.
- хэши - это неупорядоченные пары ключ-значение.
И XML не делает ни того, ни другого. У него есть элементы, которые:
- не имеет уникального имени (что означает, что хэши не "подходят").
- .... но "упорядочены" в файле.
- может иметь атрибуты (которые вы можете вставить в хеш)
- может иметь контент (но не может, но может быть унарным тегом)
- может иметь детей (любой глубины)
И эти вещи не отображаются непосредственно на доступные структуры данных perl - на упрощенном уровне может поместиться вложенный хэш хэшей - но он не может справиться с элементами с дублированными именами. Также вы не можете легко отличить атрибуты от дочерних узлов.
Так XML::Simple
пытается угадать, основываясь на содержимом XML, и получает "подсказки" от различных настроек параметров, а затем, когда вы пытаетесь вывести содержимое, он (пытается) применить тот же процесс в обратном порядке.
В результате для чего-либо, кроме самого простого XML, он в лучшем случае становится громоздким или в худшем случае теряет данные.
Рассматривать:
<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>
Это - когда анализируется через XML::Simple
дает тебе:
$VAR1 = {
'parent' => {
'child' => {
'att' => 'some_att',
'content' => 'content'
}
},
'another_node' => {
'another_child' => [
{
'some_att' => 'a value'
},
{
'different_att' => 'different_value',
'content' => 'more content'
}
]
}
};
Примечание - теперь у вас есть под parent
- просто анонимные хеши, но под another_node
у вас есть массив анонимных хэшей.
Таким образом, чтобы получить доступ к содержанию child
:
my $child = $xml -> {parent} -> {child} -> {content};
Обратите внимание, что у вас есть "дочерний" узел с узлом "контент" под ним, что не потому, что это… контент.
Но для доступа к контенту ниже первого another_child
элемент:
my $another_child = $xml -> {another_node} -> {another_child} -> [0] -> {content};
Обратите внимание, как - из-за наличия нескольких <another_node>
элементы, XML был разобран в массив, где не было ни одного. (Если у вас есть элемент с именем content
под ним, тогда вы в конечном итоге еще что-то). Вы можете изменить это с помощью ForceArray
но затем вы получите хэш массивов хэшей массивов хэшей массивов - хотя он по крайней мере последовательн в обработке дочерних элементов. Редактировать: Обратите внимание, после обсуждения - это плохое значение по умолчанию, а не недостаток XML::Simple.
Вы должны установить:
ForceArray => 1, KeyAttr => [], ForceContent => 1
Если вы примените это к XML, как указано выше, вы получите вместо этого:
$VAR1 = {
'another_node' => [
{
'another_child' => [
{
'some_att' => 'a value'
},
{
'different_att' => 'different_value',
'content' => 'more content'
}
]
}
],
'parent' => [
{
'child' => [
{
'att' => 'some_att',
'content' => 'content'
}
]
}
]
};
Это даст вам согласованность, потому что у вас больше не будет элементов одного узла обрабатывать иначе, чем несколько узлов.
Но ты все равно
- Имейте 5 ссылочных глубоких деревьев, чтобы получить значение.
Например:
print $xml -> {parent} -> [0] -> {child} -> [0] -> {content};
У тебя все еще есть content
а также child
хэш-элементы обрабатываются так, как если бы они были атрибутами, а поскольку хэши неупорядочены, вы просто не можете восстановить входные данные. Таким образом, в основном, вы должны разобрать его, а затем запустить его Dumper
выяснить, где вам нужно искать.
Но с xpath
запрос, вы получите на этом узле с:
findnodes("/xml/parent/child");
Что вы не получаете в XML::Simple
что вы делаете в XML::Twig
(и я полагаю, XML::LibXML
но я знаю это менее хорошо)
xpath
служба поддержки.xpath
XML-способ выражения пути к узлу Таким образом, вы можете "найти" узел в приведенном вышеget_xpath('//child')
, Вы даже можете использовать атрибуты вxpath
- лайкget_xpath('//another_child[@different_att]')
который выберет именно то, что вы хотели. (Вы можете перебирать совпадения тоже).cut
а такжеpaste
перемещать элементы вокругparsefile_inplace
чтобы позволить вам изменитьXML
с редактированием на месте.pretty_print
варианты, чтобы отформатироватьXML
,twig_handlers
а такжеpurge
- который позволяет вам обрабатывать действительно большой XML без необходимости загружать все это в память.simplify
если вы действительно должны сделать его обратно совместимым сXML::Simple
,- код, как правило, намного проще, чем пытаться следовать последовательным цепочкам ссылок на хэши и массивы, что никогда не может быть сделано последовательно из-за фундаментальных различий в структуре.
Это также широко доступно - легко скачать с CPAN
и распространяется как устанавливаемый пакет во многих операционных системах. (К сожалению, это не установка по умолчанию. Пока)
Смотрите: XML:: Twig краткий справочник
Для сравнения:
my $xml = XMLin( \*DATA, ForceArray => 1, KeyAttr => [], ForceContent => 1 );
print Dumper $xml;
print $xml ->{parent}->[0]->{child}->[0]->{content};
Против
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 - самый сложный из доступных анализаторов XML
Основная проблема с XML::Simple заключается в том, что с получающейся структурой крайне сложно правильно ориентироваться. $ele->{ele_name}
может вернуть любое из следующего (даже для элементов, которые следуют той же спецификации):
[ { att => 'val', ..., content => 'content' }, ... ]
[ { att => 'val', ..., }, ... ]
[ 'content', ... ]
{ 'id' => { att => 'val', ..., content => 'content' }, ... }
{ 'id' => { att => 'val', ... }, ... }
{ 'id' => { content => 'content' }, ... }
{ att => 'val', ..., content => 'content' }
{ att => 'val', ..., }
'content'
Это означает, что вы должны выполнять все виды проверок, чтобы увидеть, что вы на самом деле получили. Но сложность этого побуждает разработчиков делать очень плохие предположения.
Варианты для создания более регулярного дерева терпят неудачу
Вы можете использовать следующие параметры для создания более регулярного дерева:
ForceArray => 1, KeyAttr => [], ForceContent => 1
Но даже с этими опциями все еще требуется много проверок для извлечения информации из дерева. Например, получить /root/eles/ele
узлы из документа - это обычная операция, которая должна выполняться тривиально, но при использовании 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} };
}
В другом парсере можно использовать следующее:
my @eles = $doc->findnodes('/root/eles/ele');
XML::Simple накладывает многочисленные ограничения и не имеет общих возможностей
Это совершенно бесполезно для производства XML. Даже с
ForceArray => 1, ForceContent => 1, KeyAttr => [], KeepRoot => 1
Слишком много деталей, которыми невозможно управлять.Это не сохраняет относительный порядок детей с разными именами.
Он имеет ограниченную (с бэкэндом XML::SAX) или нет (с бэкэндом XML::Parser) поддержку пространств имен и префиксов пространства имен.
Он не может обрабатывать элементы с текстом и элементами как дочерние (что означает, что он не может обрабатывать XHTML, среди прочих).
Некоторые бэкэнды (например, XML::Parser) не могут обрабатывать кодировки, не основанные на ASCII (например, UTF-16le).
Элемент не может иметь дочерний элемент и атрибут с тем же именем.
Он не может создавать XML-документы с комментариями.
Игнорируя основные проблемы, упомянутые ранее, XML::Simple все еще можно использовать с этими ограничениями. Но зачем пытаться проверить, может ли XML::Simple обрабатывать формат вашего документа, и рискует позже переключиться на другой парсер? Вы можете просто использовать лучший парсер для всех ваших документов с самого начала.
Некоторые парсеры не только не подвергают вас этим ограничениям, но и предоставляют множество других полезных функций. Ниже приведены некоторые функции, которые у них могут быть, которых нет у XML::Simple:
Скорость. XML::Simple очень медленный, особенно если вы используете бэкэнд, отличный от XML::Parser. Я говорю на порядки медленнее, чем другие парсеры.
Селекторы XPath или аналогичные.
Поддержка чрезвычайно больших документов.
Поддержка красивой печати.
Является ли XML::Simple полезным?
Единственный формат, для которого XML::Simple является самым простым, это тот, где ни один элемент не является необязательным. У меня был опыт работы с бесчисленным количеством форматов XML, и я никогда не сталкивался с таким форматом.
Одной лишь этой хрупкости и сложности достаточно для того, чтобы избежать XML::Simple, но есть и другие.
альтернативы
Я использую XML::LibXML. Это чрезвычайно быстрый, полнофункциональный парсер. Если бы мне нужно было обрабатывать документы, которые не помещались в память, я бы использовал XML::LibXML::Reader (и его copyCurrentNode(1)
) или XML::Twig (используя twig_roots
).
Я не согласен с документами
Я не согласен и скажу, что XML::Simple
это просто.. просто. И мне всегда было легко и приятно пользоваться. Проверьте это с входным сигналом, который вы получаете. Пока вход не меняется, ты в порядке. Те же люди, которые жалуются на использование XML::Simple
пожаловаться на использование JSON::Syck
сериализовать лося. Документы ошибочны, потому что они учитывают правильность, а не эффективность. Если вы заботитесь только о следующем, вы хорошо:
- не выбрасывать данные
- построение в предоставленном формате, а не абстрактная схема
Если вы создаете абстрактный парсер, который определяется не приложением, а спецификацией, я бы использовал что-то еще. Я работал в компании один раз, и нам пришлось принять 300 различных схем XML, ни у одной из которых не было спецификации. XML::Simple
сделал работу легко. Другие варианты потребовали бы, чтобы мы фактически наняли кого-то, чтобы сделать работу. Все думают, что XML - это что-то, что отправляется в жестком, полностью охватывающем специфицированный формате формате, так что если вы пишете один парсер, вы хороши. Если это так, не используйте XML::Simple
, XML, до JSON, был просто форматом "бросай это и ходи" с одного языка на другой. Люди на самом деле использовали такие вещи, как XML::Dumper
, Никто на самом деле не знал, что было выведено. Имея дело с этим сценарием XML::Simple
это здорово! Вменяемые люди все еще сохраняют JSON без спецификации, чтобы выполнить то же самое. Просто так устроен мир.
Хотите прочитать данные в формате и не беспокоиться о формате? Хотите обойти структуры Perl, а не возможности XML? Идти XML::Simple
,
По расширению...
Аналогично, для большинства приложений JSON::Syck
достаточно сбросить это и ходить. Хотя, если вы отправляете много людей, я бы настоятельно рекомендовал не быть дураком и создавать спецификацию, в которую вы экспортируете. Но вы знаете, что... Когда-нибудь вам позвонит кто-то, с кем вы не хотите разговаривать, кому нужны его данные, которые вы обычно не экспортируете. И вы собираетесь передать это через JSON::Syck
это вуду и пусть беспокоятся об этом. Если они хотят XML? Зарядите их еще на 500 долларов и разожгите XML::Dumper
,
Увезти
Это может быть не идеально, но XML::Simple
чертовски эффективно. Каждый час, сэкономленный на этой арене, вы можете потратить на более полезную арену. Это реальный мир.
Другие ответы
У XPath есть свои плюсы. Каждый ответ здесь сводится к тому, чтобы предпочитать XPath Perl. Все в порядке. Если вы предпочитаете использовать стандартизированный специфичный для домена XML язык для доступа к вашему XML, имейте это в виду!
Perl не предоставляет простой механизм доступа к глубоко вложенным необязательным структурам.
var $xml = [ { foo => 1 } ]; ## Always w/ ForceArray.
var $xml = { foo => 1 };
Получение значения foo
здесь в этих двух контекстах может быть сложно. XML::Simple
знает это, и поэтому вы можете заставить бывшего.. Однако, что даже с ForceArray
, если элемента там нет, вы выдадите ошибку..
var $xml = { bar => [ { foo => 1 } ] };
сейчас если bar
необязательно, вы оставили доступ к нему $xml->{bar}[0]{foo}
а также @{$xml->{bar}}[0]
выдаст ошибку. Во всяком случае, это просто Perl. Это имеет 0, чтобы сделать с XML::Simple
по моему мнению. И я признал, что XML::Simple
не подходит для строительства по спец. Покажите мне данные, и я могу получить к ним доступ с помощью XML::Simple.