Perl:如何将下一个XML标记视为上一个XML标记的子标记?

时间:2018-04-30 07:09:07

标签: perl xml-parsing xml-twig

在以下数据文件中,我想将每个<Field>标记视为<Register>的子标记,将每个<Register>视为<Partition>的子标记。所以,基本上,我正在尝试使用相应的<Partition><Register>提取每个<Field>详细信息。由于所有这些标签都是独立的而不是父子关系,我如何获得所需的输出?

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

<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包,这是我的代码段:

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";
            .
            .
            .

          }
  }

2 个答案:

答案 0 :(得分:0)

根据您的评论,如果您想要将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

答案 1 :(得分:0)

顺便说一句,我已经编写了非常基本的代码,可以将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);

$df.printSchema()
root
 |-- ACTION: string (nullable = true)

$df_new.select(["ACTION"]).show()
AnalysisException: "Reference 'ACTION' is ambiguous, could be: ACTION#22058, ACTION#22334