XML :: Простое преобразование xml в perl, хеш-предупреждение «Неуникальное значение» - PullRequest
0 голосов
/ 23 сентября 2018

У меня есть правильно сформированный XML, который я пытаюсь преобразовать в хеш с помощью модуля perl XML :: Simple.В этом файле есть раздел, который не может быть проанализирован правильно.Есть ли способ (или обходной путь), чтобы правильно проанализировать xml и получить желаемый результат?

D:\tmp>perl parse_dns2.pl dns_problem_public.xml
Warning: <dns_entry> element has non-unique value in 'domain' key attribute: 
0 at parse_dns2.pl line 9.
Warning: <dns_entry> element has non-unique value in 'domain' key attribute: 
example.com at parse_dns2.pl line 9.
Warning: <dns_entry> element has non-unique value in 'domain' key attribute: 
test.com at parse_dns2.pl line 9.
$VAR1 = {
  'dns_timeout' => '20',
  'local_dns' => {
    'dns_entry' => {
      '0' => {
        'content' => '192.168.120.32'
      },
      'domain.example.com' => {
        'content' => '172.16.113.13'
      },
      'example.com' => {
        'content' => '172.16.113.13'
      },
      'test.com' => {
        'content' => '172.17.0.113'
      }
    }
  }
};

Мой код прост:

#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
use XML::Simple;
use Data::Dumper;

my $ref = XMLin(
    $ARGV[0],
    ForceArray => ['dns_entry'],
    KeyAttr    => { 'dns_entry' => 'priority' },
    KeyAttr    => { 'dns_entry' => 'domain' },
    ForceContent => 0
);

print Dumper $ref;

XML-файл (соответствующийsection) содержат атрибуты, которые мне нужно использовать в качестве ключа:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE config SYSTEM "config.dtd"> 
<dns>
    <local_dns>
        <dns_entry priority="0">192.168.120.31</dns_entry>
        <dns_entry priority="0">192.168.120.32</dns_entry>
        <dns_entry domain="example.com">172.16.103.20</dns_entry>
        <dns_entry domain="example.com">172.16.113.13</dns_entry>
        <dns_entry domain="test.com">172.17.0.111</dns_entry>
        <dns_entry domain="test.com">172.17.0.113</dns_entry>
        <dns_entry domain="domain.example.com">172.16.103.20</dns_entry>
        <dns_entry domain="domain.example.com">172.16.113.13</dns_entry>
    </local_dns>
    <dns_timeout>20</dns_timeout>
</dns>

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

Желаемый результат:

$VAR1 = {
  'local_dns' => {
    'dns_entry' => {
      'domain' => {
        'domain.example.com' => {
          'content' => [
            '172.16.113.20',
            '172.16.113.13'
          ]
        },
        'example.com' => {
          'content' => [
            '172.16.113.20',
            '172.16.113.13'
          ]
        },
        'test.com' => {
          'content' => [
            '172.17.0.111',
            '172.17.0.111'
          ]
        }
      },
      'priority' => {
        '0' => {
          'content' => [
            '192.168.120.31',
            '192.168.120.32'
          ]
        }
      }
    }
  },
  'dns_timeout' => '20'
};

1 Ответ

0 голосов
/ 23 сентября 2018

Узлы не могут иметь несколько контентов, поэтому требуется некоторое преобразование.

Вам следует воспользоваться этой возможностью, чтобы избежать использования самых сложных анализаторов XML.Так сложно использовать собственную документацию, советую против ее использования.

Вот решение XML :: LibXML:

use XML::LibXML qw( );

my $doc = XML::LibXML->new->parse_file('dns.xml');

my %data;
{
   $data{dns_timeout} = $doc->findvalue('/dns/dns_timeout/text()');

   for my $dns_entry_node ($doc->findnodes('/dns/local_dns/dns_entry')) {
      my $addr = $dns_entry_node->textContent();

      if (defined( my $priority = $dns_entry_node->getAttribute('priority') )) {
         push @{ $data{local_dns}{dns_entry}{priority}{$priority} }, $addr;
      }

      if (defined( my $domain = $dns_entry_node->getAttribute('domain') )) {
         push @{ $data{local_dns}{dns_entry}{domain}{$domain} }, $addr;
      }
   }
}
...