【问题标题】:Parsing XML with Perl用 Perl 解析 XML
【发布时间】:2013-10-17 18:00:38
【问题描述】:

这里完全是菜鸟,所以我很抱歉我的无知提前。

我搜索和弄乱的大部分内容都集中在将XML::LibXML 与 XPath 结合使用。

我遇到的问题是我不想在标签之间捕获文本:我需要标签的值。

这是我的 XML 结构

<users>
  <entry name="asd">
    <permissions>
      <role-based>
        <superuser>yes</superuser>
      </role-based>
    </permissions>
  </entry>
  <entry name="fgh">
    <permissions>
      <role-based>
        <superuser>yes</superuser>
      </role-based>
    </permissions>
    <authentication-profile>RSA Two-Factor</authentication-profile>
  </entry>
  <entry name="jkl">
    <permissions>
      <role-based>
        <superreader>yes</superreader>
      </role-based>
    </permissions>
    <authentication-profile>RSA Two-Factor</authentication-profile>
  </entry>
</users>

我正在尝试获取 name 属性(不带引号)并确定此人是超级用户还是超级阅读者。

除了打印节点之外,我无法做很多事情。我需要将其转换为用户名结构中的 CSV 文件;角色

【问题讨论】:

    标签: xml perl libxml2 xml-libxml


    【解决方案1】:

    使用XML::LibXML 从XML 文档中提取信息的最简单方法是使用find 系列方法。这些方法使用 XPath 表达式从文档中选择节点和值。以下脚本提取您需要的数据:

    use XML::LibXML;
    
    my $doc = XML::LibXML->load_xml(location => 'so.xml');
    
    for my $entry ($doc->findnodes('//entry')) {
        my $name = $entry->getAttribute('name');
        my $role = $entry->findvalue(
            'local-name(permissions/role-based/*[.="yes"])'
        );
        print("$name;$role\n");
    }   
    

    打印出来

    asd;superuser
    fgh;superuser
    jkl;superreader
    

    我使用了local-name XPath 函数来获取角色元素的名称。

    请注意,您可能希望使用Text::CSV 以更可靠的方式创建 CSV 文件。

    【讨论】:

    • 感谢您的快速回复。我得试试这个。当我尝试使用 getAttribute 调用之前它告诉我它在我的包中是未知的。我使用的是 XML:LibXML,也许我在某处有依赖问题?
    • @user2891632,如果您仍然遇到问题,请发布一个新问题,显示您正在使用的实际代码以及您遇到的错误。
    【解决方案2】:

    使用不同模块的另一种解决方案,XML::Twig

    #!/usr/bin/env perl
    
    use warnings;
    use strict;
    use XML::Twig;
    
    my ($name, $role);
    
    my $twig = XML::Twig->new(
        twig_handlers => {
            'entry' => sub { 
                $name = $_->att('name');
                if ( defined $name && defined $role ) { 
                    printf qq|%s;%s\n|, $name, $role;
                }   
                map { undef $_ } ($name, $role);
            },  
            'role-based' => sub { $role = $_->first_child->tag },
        },  
    )->parsefile( shift );
    

    像这样运行它:

    perl script.pl xmlfile
    

    产生:

    asd;superuser
    fgh;superuser
    jkl;superreader
    

    【讨论】:

      【解决方案3】:

      使用 XML::Rules:

      use XML::Rules;
      
      print "name is_superuser is_superreader\n";
      my @rules = (
        entry => sub {
          my $entry = $_[1];
          $_ ||= 'no' for @$entry{qw(superuser superreader)};
          print "$entry->{name} $entry->{superuser} $entry->{superreader}\n";
        },
        'permissions,role-based' => 'pass no content',
        'superuser,superreader' => 'content',
        _default => undef,
      );
      
      my $p = XML::Rules->new(rules => \@rules);
      my $s = $p->parse(doc());
      
      sub doc {
      return <<XML;
      <users>
         <entry name="asd">
             <permissions>
                  <role-based>
                      <superuser>yes</superuser>
                  </role-based>
             </permissions>
         </entry>
         <entry name="fgh">
             <permissions>
                  <role-based>
                      <superuser>yes</superuser>
                  </role-based>
             </permissions>
             <authentication-profile>RSA Two-Factor</authentication-profile>
         </entry>
         <entry name="jkl">
             <permissions>
                  <role-based>
                      <superreader>yes</superreader>
                  </role-based>
             </permissions>
             <authentication-profile>RSA Two-Factor</authentication-profile>
         </entry>
      </users>
      XML
      }
      

      或者一组可选的规则,假设所有内容对于您的关键字段都是“是”(以及其他一些假设):

      my $name;
      my @rules = (
        '^entry' => sub {
          $name = $_[1]->{name};
        },
        'superuser,superreader' => sub {
          print "$name,$_[0]\n";
        },
        _default => undef,
      );
      

      【讨论】:

        【解决方案4】:

        我喜欢在这样的项目中使用XML::Simple

        例如:

        use XML::Simple;
        
        my $su = $ARGV[0];
        die unless (-e $su);
        
        my $su_xml = XMLin($su, ForceArray => [ 'entry' ]);
        my $suref = $su_xml->{entry};
        
        foreach my $key (keys %{$suref}) {
            $rb = ${$suref}{$key}->{permissions}->{'role-based'};
            foreach my $rbkey (keys %{$rb}) {
                print "$key\t$rbkey\t${$rb}{$rbkey}\n";
            }
        }
        

        打印:

        fgh     superuser       yes
        asd     superuser       yes
        jkl     superreader     yes
        

        【讨论】:

        • XML::Simple 通常过于简单。例如,如果&lt;users&gt; 中只有一个&lt;entry&gt;,您的脚本就会中断。
        • @SlavenRezic 好收获!幸运的是,XML::Simple 是高度可配置的,并且通过使用ForceArray 可以轻松解决这种情况。解决方案已更新。
        猜你喜欢
        • 2011-08-08
        • 2011-03-25
        • 1970-01-01
        • 1970-01-01
        • 2013-11-15
        • 1970-01-01
        • 1970-01-01
        • 2013-07-24
        • 2017-10-28
        相关资源
        最近更新 更多