【问题标题】:XML::Simple convert xml to perl hash warning "non-unique value"XML::Simple 将 xml 转换为 perl 哈希警告“非唯一值”
【发布时间】:2018-09-23 13:34:11
【问题描述】:

我有一个格式良好的 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 文件(相关部分)包含我需要用作键的属性:

<?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'
};

【问题讨论】:

  • Re "有什么方法(或解决方法)可以正确解析 xml 并获得所需的结果?",您没有指定所需的结果是什么.
  • @ikegami:谢谢,我已经添加了一个包含所需结果的部分。基本上是一个包含所有信息的散列,因此可以使用散列表达式来处理任何元素。
  • 您确定这是您想要的结果吗?我的意思是,如果您使用findnodes,您可以使用xpath 表达式来处理任何元素,而您不必费心处理您的XML。如果您想重新How to Ask您的问题,并围绕更高层次的问题提出问题,我相信我们可以为您提供更好的答案。
  • @Sobrique:我将在我的 xml 文件的某些部分使用带有 findnodes 的 libxml。 libxml 的问题是我事先不知道 xml 文件的结构,而是需要“遍历”它。
  • 我的意思是,您可能不需要需要知道文件的结构 - 正是您要查找的内容。如果你真的需要迭代它,你可以。但您可能也不需要这样做。

标签: xml perl xml-simple


【解决方案1】:

节点不能有多个内容,因此需要进行一些转换。

您应该借此机会避免使用 XML 解析器的 most complicated。太难用了,自己的文档建议不要用。

这是一个 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;
      }
   }
}

【讨论】:

  • 同意,但是 XML::Simple 是解析具有未知结构的 xml 文件的唯一方法,方法是创建一个哈希并“遍历”它。我想我必须在我的脚本中结合 XML::Simple 和 LibXML 方式。无论如何,感谢您澄清 XML::Simple 在我的情况下是一个死胡同:-)
  • 不,不是。所有 XML 解析器都可以按照您想要的方式处理未知结构并“遍历”它。例如。 XML::LibXML 执行 childNodesXML::Twig 执行 children。当然,如果您实际上根本想要遍历XML,而是在结构中的某处找到特定的键/值,那么您可以使用findnodes 并拥有一个很多更简单_答案。
猜你喜欢
  • 2021-07-13
  • 2021-08-13
  • 2016-07-25
  • 2016-08-26
  • 1970-01-01
  • 2011-12-21
  • 1970-01-01
  • 2010-12-16
  • 1970-01-01
相关资源
最近更新 更多