Из документации XML::Simple
:
Использование этого модуля в новом коде не рекомендуется. Доступны и другие модули, которые обеспечивают более простые и последовательные интерфейсы. В частности, настоятельно рекомендуется использовать XML:: LibXML.
Основными проблемами с этим модулем являются большое количество опций и произвольные способы взаимодействия этих параметров - часто с неожиданными результатами.
Может кто-нибудь уточнить для меня, каковы основные причины этого?
Реальная проблема заключается в том, что в первую очередь пытается сделать 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};
Обратите внимание, что у вас есть "дочерний" node, под ним "контент" node, который не потому, что он... контент.
Но для доступа к содержимому под первым элементом 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'
}
]
}
]
};
Это даст вам согласованность, потому что вы больше не будете иметь отдельные элементы node, которые обрабатывают по-разному с помощью multi- node.
Но вы все еще:
Например:
print $xml -> {parent} -> [0] -> {child} -> [0] -> {content};
У вас все еще есть content
и child
хэш-элементы, обработанные так, как если бы они были атрибутами, а поскольку хеши неупорядочены, вы просто не можете восстановить вход. Итак, в основном, вам нужно разобрать его, а затем запустить через Dumper
, чтобы выяснить, где вам нужно искать.
Но с запросом xpath
вы получите node с помощью:
findnodes("/xml/parent/child");
То, что вы не получаете в XML::Simple
, которое вы делаете в XML::Twig
(и я предполагаю XML::LibXML
, но я знаю его менее хорошо):
xpath
поддержка. xpath
- это XML-способ выражения пути к node. Таким образом, вы можете "найти" node в приведенном выше примере с помощью 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:: Быстрая ссылка
Для сравнения:
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 заключается в том, что результирующая структура чрезвычайно сложна для правильной навигации. $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. Даже с 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, и я никогда не сталкивался с таким форматом.
Эта хрупкость и сложность сами по себе являются причинами, достаточными для того, чтобы гарантировать, что вы будете избегать XML:: Simple, но есть и другие.
Я использую XML:: LibXML. Это чрезвычайно быстрый, полнофункциональный синтаксический анализатор. Если мне когда-либо понадобилось обрабатывать документы, которые не вписывались в память, я бы использовал XML:: LibXML:: Reader (и его copyCurrentNode(1)
) или XML:: Twig (используя twig_roots
).
$tpp->set( force_array => [ '*' ] );
тебе нужны хотя бы my @eles; if ($doc->{root} && $doc->{root}[0]{eles} && $doc->{root}[0]{eles}[0]{ele}) { @eles = @{ $doc->{root}[0]{eles}[0]{ele} } }
чтобы получить узлы /root/eles/ele
, и предполагается, что не может быть нескольких узлов eles
. Это ничем не отличается от оптимально настроенного XML :: Simple. (Это намного хуже без force_array => [ '*' ]
.)
Я буду возражать и сказать, что XML::Simple
- это просто.. просто. И мне всегда было легко и приятно пользоваться. Проверьте его с помощью ввода, который вы получаете. Пока вход не меняется, вы в порядке. Те же люди, которые жалуются на использование XML::Simple
, жалуются на использование JSON::Syck
для сериализации Moose. Документы ошибочны, поскольку они учитывают правильность эффективности. Если вас беспокоит только следующее, вы хорошо:
Если вы создаете абстрактный парсер, который не определен приложением, а по спецификации, я бы использовал что-то еще. Я работал в компании один раз, и нам пришлось принять 300 различных схем XML, ни один из которых не имел спецификации. XML::Simple
легко выполнил работу. Другие варианты потребовали бы, чтобы мы наняли кого-то, чтобы выполнить работу. Все думают, что XML - это то, что отправлено в жестком всеохватывающем специфицированном формате, так что если вы напишете один парсер, вы добры. Если в этом случае не используется XML::Simple
. XML, до JSON, был всего лишь "дампом этого и гуляющего" формата с одного языка на другой. Люди действительно использовали такие вещи, как XML::Dumper
. Никто не знал, что вышло. Работа с этим сценарием XML::Simple
is greattt! Сэйнские люди все еще сбрасывают JSON без спецификации для достижения того же самого. Это как работает мир.
Хотите прочитать данные и не беспокоиться о формате? Хотите пересечь структуры Perl, а не возможности XML? Перейти XML::Simple
.
Аналогично, для большинства приложений JSON::Syck
достаточно сбрасывать это и ходить. Хотя, если вы отправляете много людей, я бы предложил вам не использовать сопло для душа и делать спецификацию, на которую вы экспортируете. Но, вы знаете, что.. Когда-нибудь вы получите звонок от кого-то, с кем вы не хотите говорить, кто хочет, чтобы его данные не экспортировались обычно. И вы собираетесь пропустить его через JSON::Syck
voodoo и позволить им беспокоиться об этом. Если они хотят XML? Зарядите их еще на 500 долларов и запустите ole XML::Dumper
.
Он может быть менее совершенным, но XML::Simple
эффективен. Каждый час, сэкономленный на этой арене, вы можете потратить на более полезную арену. Это реальное мировоззрение.
У Look 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]
выдаст ошибку. Во всяком случае, это просто перл. Это связано с XML::Simple
imho. И я признал, что XML::Simple
не подходит для построения спецификации. Покажите мне данные, и я могу получить к нему доступ с помощью XML:: Simple.