Как получить значение из глубины хэша в Perl

Я читаю в XML-файле,

$myxml = XMLin("$configfile");

И когда я печатаю его с помощью Dumper (print Dumper($myxml);), я получаю это...

$VAR1 = {
          'Timeout' => 5,
          'Roots' => {
                        'Root' => [
                                     {
                                       'Name' => 'Sales',
                                       'Level' => 'Indeterminate',
                                       'Profiles' => {
                                                    'Profile' => [
                                                                {
                                                                  'Name' => 'Bill',
                                                                  'Age' => '50',
                                                                  'Status' => Active
                                                                },
                                                                {
                                                                  'Name' => 'Bob',
                                                                  'Age' => '24',
                                                                  'Status' => Inactive
                                                                }
                                                              ]
                                                  },
                                       'Interval' => 'Order',
                                       'Action' => 'Reject'
                                     },
                                     {
                                      'Name' => 'User',
                                      'Level' => 'Indeterminate',
                                      'Profiles' => {
                                                   'Profile' => [
                                                            {
                                                              'Name' => 'User',
                                                              'Action' => 'Reject',
                                                              'User' => 'acount'
                                                             }, 
                                                            {
                                                              'Name' => 'Admin',
                                                              'Action' => 'Accept',
                                                              'User' => 'acount'
                                                             },                                                                   
                                   ]
                      }
        };    

Я хотел бы прочитать этот хэш и получить значение всех неактивных "Статус" или или получить "Статус Боба".

{
'Name' => 'Bob',
'Age' => '24',
'Status' => Inactive
}

Начать редактирование:

Таким образом, чтобы получить информацию профиля для одного человека..

Dumper($myxml->{'Roots'}->{'Root'}[0]{'Profiles'}{'Profile'}[2]); 

Например, чтобы получить статус для Боба

if ($myxml->{'Roots'}->{'Root'}[0]{'Profiles'}{'Profile'}[1]{'Name'} eq "Bob") {
$status = $myxml->{'Roots'}->{'Root'}[0]{'Profiles'}{'Profile'}[1]{'Status'};
}

Тем не менее, как мне пройти через этот xml, чтобы он продолжал проверять {'Roots'}->{'Root'} и {'Profiles'}{'Profile'}, если Боб не находится в местоположении [0] и [1]. Двойной цикл foreach?

Конец Править

Я включил пример XML..

<Root Name="Sales" Level="Indeterminate" Profile="Order" Interval="Order" Action="Reject">
  <Profiles>
    <Profile Name="Bill" Age="50" Status=Active />
    <Profile Name="Bob" Age="24" Status=InActive />
    <Profile Name="Ben" Age="45" Status=Active />
  </Profiles>
</Root>

Который производит это:

$VAR1 = {
      'Name' => 'Sales',
      'Type' => 'Indeterminate',
      'Profiles' => {
                   'Profile' => [

                               {
                                 'Name' => 'Bill',
                                 'Age' => '50',
                                 'Status' => Active
                               },
                               {
                                 'Name' => 'Bob',
                                 'Age' => '24',
                                 'Status' => InActive
                               },
                               {
                                 'Name' => 'Ben',
                                 'Age' => '45',
                                 'Status' => Active
                               }
                             ]
                 },
      'Interval' => 'Order',
      'Action' => 'Reject'
    };

Спасибо,

Джон.

3 ответа

У вас есть ответ, как это сделать с XML::Simple уже. Но я бы предложил, а не использовать XML::Twig вместо этого, который НАМНОГО менее противен.

Почему XML::Simple "не рекомендуется"?

Я собираюсь предположить, что ваш XML выглядит примерно так:

<opt Timeout="5">
  <Roots>
    <Root Action="Reject" Interval="Order" Level="Indeterminate" Name="Sales">
      <Profiles>
        <Profile Age="50" Name="Bill" Status="Active" />
        <Profile Age="24" Name="Bob" Status="Inactive" />
      </Profiles>
    </Root>
  </Roots>
</opt>

Я не могу сказать наверняка, потому что это радость XML::Simple, Но:

#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;

my $twig = XML::Twig -> new -> parsefile ( $configfile );

print $twig -> get_xpath ( '//Profile[@Name="Bob"]',0 ) -> att('Status')

Это использует xpath найти атрибут, который вы хотите - // обозначает поиск в любом месте дерева.

Но вы могли бы вместо этого:

print $twig -> get_xpath ( '/opt/Roots/Root/Profiles/Profile[@Name="Bob"]',0 ) -> att('Status')

Гораздо проще, вы не согласны?

Или повторить все "Профили":

foreach my $profile ( $twig -> get_xpath ('//Profile' ) ) {
    print $profile -> att('Name'), " => ", $profile -> att('Status'),"\n";
}

Это одна из причин того, что XML:: Simple настоятельно не рекомендуется - иногда он будет выдавать ссылку на массив, когда вы ожидаете ссылку на хеш.

Вы можете видеть, что значение $rulesxml->{'Roots'}->{'Root'} ссылка на массив, а не ссылка на хеш, так как она начинается с [ не {,

Если данные именно то, что вы показываете здесь, то все, что вам нужно сделать, это вставить в ваш код поиск по массиву.

Dumper($myxml->{'Roots'}->{'Root'}->[0]->{'Profiles'}->{'Profile'}); 

Я использовал 0, так как есть (в настоящее время?) Только один элемент в этом массиве. Если ваши данные более сложны и у вас есть несколько элементов в массиве, то вам нужно будет использовать цикл.

Для такой работы я настоятельно рекомендую потратить время на изучение XPath и использовать модуль, который это поддерживает (мне нравится XML:: LibXML).

Несколько других способов упростить ваш код:

  1. Вы цитируете переменную в XMLin() звонить без необходимости.

    $myxml = XMLin($configfile); # Works fine without quotes

  2. Вы можете опустить промежуточные стрелки в многоуровневых поисках.

    $myxml->{'Roots'}{'Root'}[0]{'Profiles'}{'Profile'}

  3. Вы также можете опустить большинство кавычек вокруг имен ключей хеша.

    $myxml->{Roots}{Root}[0]{Profiles}{Profile}

Обновление: я не хочу дать вам решение вашего добавленного вопроса, потому что (как мы не устаем здесь указывать) XML::Simple - ужасное решение большинства проблем XML.

Но, учитывая вашу структуру данных (или, вернее, версию вашей структуры данных, которую я очистил, чтобы она на самом деле компилировалась!), Это то, как вы должны проходить ее (да, это вложенный цикл).

for my $root (@{ $myxml->{Roots}{Root} }) {
  for my $profile (@{ $root->{Profiles}{Profile} }) {
    if ($profile->{Name} eq 'Bob') {
      say $profile->{Status};
    }
  }
}

Но, пожалуйста, не используйте этот подход. Подход XML::Twig, предложенный Sobrique, является гораздо лучшим ответом. И подход XPath, основанный на XML:: LibXML, будет аналогичным.

Всякий раз, когда вы извлекаете данные из XML-документа, XPath, вероятно, будет лучшим решением. Если вы имеете дело с XML, то вам действительно нужно иметь XPath в своем наборе инструментов.

Значение Root является ArrayRef. Вы игнорируете массив и рассматриваете его так, как если бы он был одним из HashRef, которые находятся внутри него. Вам нужно перебрать массив или получить к нему доступ с определенным индексом.

Другие вопросы по тегам