【问题标题】:Perl: How to consider next XML tag as child tag of previous one?Perl:如何将下一个 XML 标签视为前一个的子标签?
【发布时间】:2018-10-10 06:07:37
【问题描述】:

在以下数据文件中,我想将每个 <Field> 标签视为 <Register> 的子标签,并将每个 <Register> 视为 <Partition> 的子标签。所以,基本上,我试图用相应的<Register><Field> 提取每个<Partition> 细节。由于所有这些标签都是独立的,而不是父子关系,我怎样才能得到我想要的输出?

由于文件非常大,我不想让它成为父子关系,因为它需要查找/替换和手动干预。

<Partition>
    <Name>1</Name>
    <Abstract>2</Abstract>
    <Description>3</Description>
    <ParentName>4</ParentName>

    </Partition>
    <Partition>
    <Name>8</Name>
    <Abstract></Abstract>
    <Description>9</Description>
    <ParentName>10</ParentName>

    </Partition>
    <Register>
    <Name>12</Name>
    <Abstract></Abstract>
    <Description>13</Description>
    <ParentName>14</ParentName>

    <Size>32</Size>
    <AccessMode>15</AccessMode>
    <Type>16</Type>


    </Register>
    <Field>
    <Name>17</Name>
    <Abstract></Abstract>
    <Description></Description>
    <ParentName></ParentName>


    </Field>
    <Field>
    .
    .
    .
    </Field>
    <Register>
    .
    .
    .

    </Register>
    <Field>
    .
    .
    .

    </Field>
    <Field>
    .
    .
    .
    </Field>
    <Partition>
        <Name>88</Name>
        <Abstract></Abstract>
        <Description></Description>
        <ParentName>55</ParentName>

    </Partition>
    <Register>
        .
        .
        .

    </Register>
    <Field>
        .
        .
        .

    </Field>
    <Partition>
        .
        .
        .
    </Partition>
    <Partition>
        .
        .
        .
    </Partition>
    <Partition>
       .
       .
       .
    </Partition>
    <Register>
        .
        .
        .
    </Register>

我正在使用XML::Twig 包,这是我的代码 sn-p:

foreach my $register ( $twig->get_xpath('//Register') ) # get each <Register>
    {
        #print $register, "\n";
        my $reg_name = $register->first_child('Name')->text;
        my $reg_abstract= $register->first_child('Abstract')->text;
        my $reg_description= $register->first_child('Description')->text;
       .
       .
       .
          foreach my $xml_field ($register->get_xpath('Field'))
          {
            my $reg_field_name= $xml_field->first_child('Name')->text;
            my $reg_field_abstract= $xml_field->first_child('Abstract')->text;
            #print "$reg_field_name \n";
            .
            .
            .

          }
  }

【问题讨论】:

  • 我同意他们应该是孩子,但他们不是。再多的“考虑”(无论这意味着什么)都不会改变这一事实。兄弟姐妹不是孩子,不就是用next_sibling代替first_child吗?
  • 正如 ikegami 所说,RegisterField 不是 Partition 的孩子。另外,您似乎无论如何都将整个文件加载到内存中。如果文件真的很大,你可以做的是在所有 3 个标签上设置twig_handlers,根据当前元素和前一个元素输出/存储信息,并在找到新的 @ 时清除树987654337@。请注意,如果没有适当的测试数据,很难为您提供代码示例(特别是我想知道父/子关系是否不应由 ParentName 元素而不是 XML 中元素的位置来定义)
  • 这是个好主意。如何将此文件重写为父子关系?

标签: perl xml-parsing xml-twig


【解决方案1】:

根据您的评论,如果您想使用 RegisterField 元素作为 Partition 元素的子元素重写文件,您可以执行以下操作:

最简单的解决方案,整个文件加载到内存中:

#!/usr/bin/env perl

use strict;
use warnings;

use XML::Twig;

my $test_file= 'test.xml';

XML::Twig->new( twig_handlers => { 'Register|Field' => \&child,
                                 },
                pretty_print => 'indented',
              )
          ->parsefile( $test_file)
          ->print;

sub child
  { my( $t, $child)= @_;
    $child->move( last_child => $child->prev_sibling( 'Partition'));
  }

由于您提到文件可能非常大,下面是一个稍微复杂一点的版本,它仅在内存中保留 2 个Partition 元素(包括第一个元素的新子元素)。当解析Partition 时,它使用flush_up_to 刷新树,直到之前的Partition

#!/usr/bin/env perl

use strict;
use warnings;

use XML::Twig;

my $test_file= 'test.xml';

XML::Twig->new( twig_handlers => { 'Partition' => \&parent,
                                   'Register|Field' => \&child,
                                 },
                pretty_print => 'indented',
              )
          ->parsefile( $test_file);

sub child
  { my( $t, $child)= @_;
    $child->move( last_child => $child->prev_sibling( 'Partition'));
  }

sub parent
  { my( $t, $partition)= @_;
    if( my $prev_partition = $partition->prev_sibling( 'Partition'))
      { $t->flush_up_to( $prev_partition); }
  }

请注意,由于使用了flush_up_to,因此在解析结束时会自动刷新树的其余部分

如果您需要将 XML 写入特定文件,而不是 STDOUT,您还可以将文件句柄传递给 flush_up_to

【讨论】:

  • 谢谢米罗德!我面临这个错误:第 11 行第 0 列第 333 个字节的文档元素后出现垃圾,位于 C:/Perl64/lib/XML/Parser.pm 第 187 行。1. &lt;Partition&gt; 2. &lt;Name&gt;PP&lt;/Name&gt; 3. &lt;Abstract&gt;&lt;/Abstract&gt; 4. &lt;Description&gt;&lt;/Description&gt; 5. &lt;ParentName&gt;sec&lt;/ParentName&gt; 6. &lt;StructureTag&gt;&lt;/StructureTag&gt; 7. &lt;AddressOffset&gt;64&lt;/AddressOffset&gt; 8. &lt;NumberOfPartitions&gt;1&lt;/NumberOfPartitions&gt; 9. &lt;IntervalBetweenPartitions&gt;0&lt;/IntervalBetweenPartitions&gt; 10. &lt;/Partition&gt; 11. &lt;Partition&gt; 12. &lt;Name&gt;control_alarm_status&lt;/Name&gt; 13. &lt;Abstract&gt;&lt;/Abstract&gt; 14. &lt;Description&gt;#&lt;/Description&gt;
  • 好的。解决了。我的文件中缺少根元素标记。谢谢@mirod。
【解决方案2】:

顺便说一句,我已经编写了非常基本的代码来将 Field 作为孩子转换为 Register 并将 Register 作为孩子转换为 Partition

use strict;
#use warnings;
use XML::Twig;
use Data::Dumper; 
use Data::Alias;

my $input_xml_file = "gpon.xml";

open (IN_FILE,$input_xml_file);
my @input_file = <IN_FILE>;


for (my $line=0;$line<@input_file;$line++)
{

            if ($input_file[$line] =~ /<\/Partition>/ && $input_file[$line+1] =~ /<Register>/)
            {
                $input_file[$line] = '';

            }
            if ($input_file[$line] =~ /<\/Field>/ && $input_file[$line+1] =~ /<Partition>/)
            {
                $input_file[$line] = "</Field>
</Register>
</Partition>
";

            }
            if ($input_file[$line] =~ /<\/Field>/ && $input_file[$line+1] =~ /<Register>/)
            {
                $input_file[$line] = "</Field>
</Register>
";

            }
            if ($input_file[$line] =~ /<\/Register>/ && $input_file[$line+1] =~ /<Field>/ )
            {
                $input_file[$line] = '';

            }

}
#print OUT_FILE "</Register>";


close(IN_FILE);
open (OUT_FILE,'>gpon_modified.xml');
foreach (@input_file)
{
     print OUT_FILE "$_";
}
print OUT_FILE "</Register>
</Partition>";
close (OUT_FILE);

【讨论】:

    猜你喜欢
    • 2015-12-22
    • 2019-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-18
    • 2014-07-09
    • 2011-10-28
    相关资源
    最近更新 更多