Perl не интерпретирует значение в тактах, называя его неопределенным значением

#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 было хорошим. После дальнейшего изучения модуля я думаю, что это немного излишне для очень простого синтаксического анализа, который мне здесь нужен. Еще раз спасибо!