通过行走DOM树和使用属性生成两个单独的XML文件

时间:2018-04-24 02:33:14

标签: xml perl xml-libxml

我需要使用可选的属性值将xml代码分隔为两个单独的文件。我更喜欢使用Perl。

来使用XML :: LibXML DOM方法
Sample XML code excerpt:
...
<LocalJMS>
  <Name>ZLAT</Name>
  <PrimaryConnection>
    <Address string="ops">ops.zla</Address>
    <Address string="spt">spt.zla</Address>
    <Port>77777</Port>
  </PrimaryConnection>
  <SecondaryConnection string="ops">
    <Address>abc.zla</Address>
    <Port>77777</Port>
  </SecondaryConnection>
</LocalJMS>

所需的两个最终xml文件将是:

1.) OPS file:
...
 <LocalJMS>
   <Name>ZLAT</Name>
   <PrimaryConnection>
     <Address>ops.zla</Address>
     <Port>77777</Port>
   </PrimaryConnection>
   <SecondaryConnection>
     <Address>abc.zla</Address>
     <Port>77777</Port>
   </SecondaryConnection>
 </LocalJMS>

2.) SPT file:
...
 <LocalJMS>
   <Name>ZLAT</Name>
   <PrimaryConnection>
     <Address>spt.zla</Address>
     <Port>77777</Port>
   </PrimaryConnection>
 </LocalJMS>

在生成两个最终的xml文件之前删除属性没有问题/问题,对于没有子元素的属性做出决定也没有任何问题 - 我可以处理那个当我遍历DOM树并检查子节点时,xml内容到正确的最终xml文件。

但我遇到的问题是在子元素中定义属性的时候(例如'SecondaryConnection',它是'LocalJMS'的子节点)。 如果我“走”DOM树,我将首先遇到父元素'LocalJMS',我需要它的一些子元素(例如'Name','PrimaryConnection')去两个最终文件,但后来我只需要'SecondaryConnection'元素只能转到OPS xml文件(不是SPT文件)。 [btw,该属性适用于所有子节点,即'Address'&amp; '端口']

我正在寻找一些想法 - 可能使用parse_balanced_chunk或从最初的xml文件的最深部分开始工作并向外工作,循环到每个子节点。我讨厌必须使用传统的grep模式等,并将xml文件视为一个简单的文本文件 - 我希望利用DOM方法。

2 个答案:

答案 0 :(得分:0)

我建议您解析原始XML,然后对于string属性的每个值,您可以克隆整个文档,并删除所有具有string的任何值的属性的元素。需要的那个

看起来像这样。我相信你能够在必要时将输出更改为更合适的输出

use strict;
use warnings 'all';

use XML::LibXML;

my $dom = XML::LibXML->load_xml( location => 'sample.xml' );

for my $string ( qw/ ops spt / ) {

    print "\$string = $string\n\n";

    my $copy = $dom->cloneNode(1);

    for my $unwanted ( $copy->findnodes("//*[\@string != '$string']") ) {
        my $parent = $unwanted->parentNode;
        $parent->removeChild($unwanted);
    }

    print $copy, "\n\n---\n\n";
}

输出

$string = ops

<?xml version="1.0"?>
<LocalJMS>
  <Name>ZLAT</Name>
  <PrimaryConnection>
    <Address string="ops">ops.zla</Address>

    <Port>77777</Port>
  </PrimaryConnection>
  <SecondaryConnection string="ops">
    <Address>abc.zla</Address>
    <Port>77777</Port>
  </SecondaryConnection>
</LocalJMS>


---

$string = spt

<?xml version="1.0"?>
<LocalJMS>
  <Name>ZLAT</Name>
  <PrimaryConnection>

    <Address string="spt">spt.zla</Address>
    <Port>77777</Port>
  </PrimaryConnection>

</LocalJMS>


---

[Finished in 0.8s]

答案 1 :(得分:-2)

它不是您想要的XML解析,但似乎适用于您的示例数据。具有string="filename"属性的标记仅包含该文件(其中文件名为大写),并删除了string属性。所有其他标签都会进入所有文件:

my $input=join"",<DATA>;
my @string=$input=~/ string="(\w+)"/g;
for my $s (@string){
    my $output=$input;
    $output=~
      s{ (\s*) <(\w+)\s* ([^>]*?) string="(\w+)" (.*?</\2>) }
       { $4 eq $s ? "$1<$2$3$5" : ""                        }gsex;
    open my $FH, '>', uc($s) or die;
    print $FH $output;
    close($FH)
}
__DATA__
<LocalJMS>
  <Name>ZLAT</Name>
  <PrimaryConnection>
    <Address string="ops">ops.zla</Address>
    <Address string="spt">spt.zla</Address>
    <Port>77777</Port>
  </PrimaryConnection>
  <SecondaryConnection string="ops">
    <Address>abc.zla</Address>
    <Port>77777</Port>
  </SecondaryConnection>
</LocalJMS>

输出:

$ cat OPS 
<LocalJMS>
  <Name>ZLAT</Name>
  <PrimaryConnection>
    <Address>ops.zla</Address>
    <Port>77777</Port>
  </PrimaryConnection>
  <SecondaryConnection>
    <Address>abc.zla</Address>
    <Port>77777</Port>
  </SecondaryConnection>
</LocalJMS>

$ cat SPT
<LocalJMS>
  <Name>ZLAT</Name>
  <PrimaryConnection>
    <Address>spt.zla</Address>
    <Port>77777</Port>
  </PrimaryConnection>
</LocalJMS>