【问题标题】:Navigating XML to access CDATA using XML::TWIG使用 XML::TWIG 导航 XML 以访问 CDATA
【发布时间】:2019-08-14 09:42:23
【问题描述】:

我有这个 XML 文件,我需要一次访问一个特定节点。下面是我的 XML 示例以及示例代码。

我的代码工作正常,只是我循环遍历所有消息/内容标签,而不是只获取当前消息标签下的特定消息/内容标签。例如,当我只希望返回 1 个 () 时,我会在处理当前消息标签(带有 refid="123991123" 的那个)时返回 3 个消息/内容标签。希望这是有道理的。如有任何帮助,我们将不胜感激。

代码:

my $twig = XML::Twig->new(
twig_handlers => {
    Selection => sub {
        foreach my $message ($_->findnodes('./Contents/Message')) {

            if($message->att('custom')){
                $Message_custom = $message->att('custom');
                foreach my $Content ($_->findnodes('./Contents/Message/Content')) {
                    print $Selection_id.": ".$Message_refid.": ".$TotalContents++."\n";
                    if($Content->att('language') eq "en"){
                        if($Content->att('imagelibraryid')){
                            $Message_Content_language_en_imagelibraryid = $Content->att('imagelibraryid');
                        }else{
                            $Message_Content_language_en = substr($message->field('Content'), 0, 20);
                        }
                    }
                }
            }
        }
    },
}
);

XML:

<?xml version="1.0" encoding="UTF-8"?>
<Root>
  <Selection id="54008473">
    <Name>Master</Name>
    <Contents>
      <Message refid="125796458" suppress="true" status="Unchanged"/>
      <Message refid="123991123" suppress="true" status="Unchanged">
        <Content language="en" imagelibraryid="5492396"/>
      </Message>
      <Message refid="128054778" custom="true" status="New">
        <Content language="en"><![CDATA[<p>Some English content</p>]]></Content>
        <Content language="fr"><![CDATA[<p>Some French content</p>]]></Content>
      </Message>
    </Contents>
  </Selection>
  <Selection id="54008475" datavaluerefid="54008479">
    <Name>RMBC</Name>
    <Contents>
      <Message refid="125796458" sameasparent="true" parentrefid="54008473" status="Unchanged"/>
      <Message refid="123991123" sameasparent="true" parentrefid="54008473" status="Unchanged"/>
      <Message refid="128054778" custom="true" status="New">
        <Content language="en"><![CDATA[<p>ada</p>]]></Content>
      </Message>
    </Contents>
  </Selection>
</Root>

【问题讨论】:

  • 您的代码不会在严格/警告下编译,并且永远不会设置 $Message_refid。请修复此问题,以便对其进行调试。
  • 我很难理解你想让你的代码做什么。你想获得什么输出?

标签: xml perl xml-twig


【解决方案1】:

这是第一次尝试根据 XML 的结构来尝试理解您的代码应该做什么:

  • Selection 节点的处理程序在MessageContentnodes 下查找具有language == 'en' 属性的子Content 节点
    • 转换为 XPath ./Contents/Message/Content[@language='en']
    • 如果它有一个属性imagelibraryid,存储它的值
    • 否则存储第一个孩子的CDATA 内容
    • refid设置为父节点Message的属性值
  • 将它们附加到 Selection 节点的内容列表中
  • 要显示收集的内容,请在数组 ref 上使用 Data::Dumper
#!/usr/bin/perl
use warnings;
use strict;

use XML::Twig;
use Data::Dumper;

my %selections;

my $twig = XML::Twig->new(
    twig_handlers => {
        Selection => sub {
            #$_->print();
            print "selection id: ", $_->att('id'), "\n";

            my @contents;
            foreach my $content ($_->findnodes("./Contents/Message/Content[\@language='en']")) {
                my $result = {
                    refid => $content->parent->att('refid'),
                };
                my $id     = $content->att('imagelibraryid');
                if (defined $id) {
                    $result->{library} = $id;
                } else {
                    $result->{cata}    = $content->first_child->cdata;
                }
                push(@contents, $result);
            }

            # store collected Content nodes under selection ID
            $selections{ $_->att('id') } = \@contents;
        },
    }
);

$twig->parse(\*DATA);

while (my($id, $contents) = each %selections) {
    my $dump = Dumper($contents);
    print "Selection '${id}' messages: $dump\n";
}

exit 0;

__DATA__
<?xml version="1.0" encoding="UTF-8"?>
... the rest of your XML left out ...

试运行:

$ perl dummy.pl
selection id: 54008473
selection id: 54008475
Selection '54008473' messages: $VAR1 = [
          {
            'refid' => '123991123',
            'library' => '5492396'
          },
          {
            'cata' => '<p>Some English content</p>',
            'refid' => '128054778'
          }
        ];

Selection '54008475' messages: $VAR1 = [
          {
            'cata' => '<p>ada</p>',
            'refid' => '128054778'
          }
        ];

【讨论】:

  • CDATA 指令更新了答案。我希望这就是您想要的。
  • 大家好,很抱歉我的问题不清楚。可悲的是……我认为这很清楚。以后我会尽量讲清楚。话虽如此,Stefan,我认为你是唯一懂我语言的人:)。不知道这是好消息还是坏消息。无论如何,您的代码几乎是正确的。我唯一添加的是 [\@refid='$Message_refid'] 以仅隔离特定消息节点下的内容项(请参见下面的代码): foreach my $Content ($_->findnodes("./Contents/Message[\ @refid='$Message_refid']/Content[\@language='en']")) { 谢谢!我从你身上学到了很多。安杰洛
猜你喜欢
  • 2019-08-03
  • 1970-01-01
  • 1970-01-01
  • 2020-11-27
  • 1970-01-01
  • 1970-01-01
  • 2013-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多