#xml #perl #xml-simple
#xml #perl #xml-простой
Вопрос:
Я пишу подпрограмму, которая использует XML::Simple
модуль для извлечения информации из файла конфигурации.
XML-файл имеет следующую структуру
<main>
<server hostname="blahblah" ... more_attributes="more"/>
<server etc./>
<server etc./>
</main>
Код работает нормально. Он помещает XML-данные во вложенный хэш, как и ожидалось. Но когда я хочу изолировать один сервер от остальных, используя текущую систему hostname
, я сталкиваюсь с проблемами.
Я думаю, что эта строка
my %systemHash = %{$xmlObject->{SERVER}->{`hostname`}};
следует вставить имя хоста текущего компьютера в последний набор скобок. Однако при выполнении кода я получаю
Can't use an undefined value as a HASH reference
Комментарии:
1. Можете ли вы поделиться кодом, который считывает XML? Существует несколько вариантов, которые изменяют поведение XML::Simple .
2. Чтобы уточнить: с параметрами по умолчанию,
XMLin
дает мне хэш массива хэшей, а не вложенных хэшей.3. @JimDavis: Больше всего важно соблюдать пожелания автора, Гранта Маклина,
XML::Simple
модуля, который говорит, что «Использование этого модуля в новом коде не рекомендуется»4. Кстати, модуль ядра Perl
Sys::Hostname
экспортирует собственную функциюhostname
, которая работает без необходимости использования обратных ссылок или внешних команд
Ответ №1:
Прежде всего, пожалуйста, не используйте XML::Simple
. В его собственной документации говорится об этом
Использование этого модуля в новом коде не рекомендуется. Доступны другие модули, которые обеспечивают более простые и согласованные интерфейсы. В частности, настоятельно рекомендуется использовать XML::LibXML.
Основными проблемами этого модуля являются большое количество опций и произвольные способы взаимодействия этих опций — часто с неожиданными результатами.
Вы также должны проверять результат любого вызова подпрограммы или использования команды оболочки Data::Dump
. Это будет выглядеть так
perl -MData::Dump -E'dd `hostname`'
который в моей системе показывает
"Samurai-Un"
Надеюсь, теперь вы видите проблему? Строка, возвращаемая обратными метками, имеет завершающий символ новой строки, и в вашем $xmlObject
хэше есть элемент, который имеет подобный ключ. Вы можете исправить это с помощью
chomp(my $host = `hostname`)
после чего вы могли бы написать
my %systemHash = %{ $xmlObject->{SERVER}{$host} }
Наконец, расточительно копировать весь первый уровень хэша, как вы делаете здесь
my %systemHash = %{$xmlObject->{SERVER}->{`hostname`}}
Вы не продолжаете показывать, для чего вы хотите использовать эту информацию, но в целом гораздо лучше извлечь ссылку на хэш, используя
chomp( my $hostname = `hostname` );
my $systemHash = $xmlObject->{SERVER}{$hostname};
Обновить
Было бы намного лучше использовать надлежащий модуль синтаксического анализа XML.
Вот пример решения, использующего XML::LibXML
use strict;
use warnings;
use 5.010; # For 'say'
use XML::LibXML;
my ($xml_file) = @ARGV;
my $xml = XML::LibXML->load_xml(location => $xml_file);
my @servers = $xml->findnodes('/main/server');
my @server_names = map $_->findvalue('@hostname'), @servers;
say "- $_" for @server_names;
входной файл
<main>
<server hostname="server1" more_attributes="more"/>
<server hostname="server2" more_attributes="more"/>
<server hostname="server3" more_attributes="more"/>
</main>
вывод
- server1
- server2
- server3
Комментарии:
1. спасибо за пример perl -mData::Dump -E’dd
hostname
. Я никогда не сталкивался с этим, очень полезно.
Ответ №2:
Вы должны использовать Data::Dumper для выгрузки выходных данных из XML::Simple следующим образом:
use Data::Dumper;
# Retrieve your data structure via XML::Simple. Then...
print Dumper $xmlObject;
Вы увидите, что он не создает структуру, которую вы ожидаете, поэтому вы получаете Can't use an undefined value as a HASH reference
сообщение. Это сообщение означает, что либо $xmlObject
не определено, либо $xmlObject->{SERVER}
не определено. perldiag описывает ошибку как:
Значение, используемое либо как жесткая ссылка, либо как символическая ссылка, должно быть определенным значением. Это помогает избежать некоторых коварных ошибок.
Вы обрабатываете неопределенное значение как ссылку на хэш. И, как уже упоминалось, причина, вероятно, в том, что созданная структура XML::Simple отличается от ваших ожиданий.
XML::Simple не так прост, как следует из его названия, и как можно было бы надеяться.И в настоящее время его собственная документация препятствует его использованию. Существуют параметры конфигурации, которые помогают нормализовать структуру, которую он создает, но, не видя больше вашего XML и кода, который вы используете для его чтения, это самое подробное объяснение, которое я могу дать. Лучший совет в отношении того, как вы анализируете свой XML, — использовать более надежный модуль. Двумя очень распространенными и хорошо зарекомендовавшими себя альтернативами являются XML ::Twig и XML :: LibXML.
В вашем коде есть дополнительная ошибка, с которой вы столкнетесь дальше: имя хоста, возвращаемое вашим системным вызовом « hostname
, имеет новую строку в конце, поэтому вы запрашиваете хэш-ключ с именем:
"somehostn"
… когда вы действительно хотите просто « somehost
«. Я подозреваю, что вы на это не рассчитываете. Как минимум, вам понадобится что-то вроде этого:
chomp( my $h = `hostname` );
my %systemHash = %{$xmlObject->{SERVER}->{$h}};
Или это:
use Sys::Hostname;
my %systemHash = %{$xmlObject->{SERVER}->{hostname()};
Я бы предпочел последнее, поскольку оно более переносимо, а модуль Sys::Hostname поставляется с Perl, так что он уже есть в вашей системе. Его вывод не потребует обработки.
Комментарии:
1. Спасибо @DavidO за этот ответ. В то время как другие, возможно, предоставили аналогичную информацию, вы ответили на актуальный вопрос, который я задавал, и предложили другой вариант. В этом случае использование
Sys::Hostname
иhostname()
решило мою проблему. Предложение относительноXML::LibXML
было хорошим. После дальнейшего изучения модуля я думаю, что это немного излишне для очень простого синтаксического анализа, который мне здесь нужен. Еще раз спасибо!