Создайте два отдельных файла XML, пройдясь по дереву DOM и используя атрибуты

Мне нужно разделить XML-код на два отдельных файла с помощью необязательных значений атрибутов. Я предпочитаю использовать методы XML::LibXML DOM, используя Perl.

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-файлов, и при этом у меня нет проблем с принятием решения об элементе с атрибутом, который не имеет дочерних элементов - я могу справиться с этим, пока прокачка содержимого XML в правильный конечный файл XML, когда я иду по дереву DOM и проверяю дочерние узлы.

Но проблема, с которой я сталкиваюсь, заключается в том, что атрибут определен внутри дочернего элемента (например, "SecondaryConnection", который является дочерним по отношению к "LocalJMS"). Если я "пройдусь" по дереву DOM, я сначала столкнусь с родительским элементом "LocalJMS", и мне понадобятся некоторые из его дочерних элементов (например, "Имя", "PrimaryConnection"), чтобы перейти к обоим конечным файлам, но тогда мне нужно только элемент 'SecondaryConnection' для перехода только к XML-файлу OPS (не к файлу SPT). [между прочим, атрибут применим ко всем дочерним узлам, т.е. "Адрес" и "Порт"]

Я ищу некоторые идеи - может быть, используя parse_balanced_chunk или работать с самой глубокой частью исходного XML-файла и работать вне, циклически проходя через каждый дочерний узел. Я ненавижу, как черт возьми, использовать традиционные шаблоны grep и т. Д. И рассматривать XML-файл как простой текстовый файл - я надеялся воспользоваться преимуществами методов DOM.

2 ответа

Я предлагаю вам проанализировать исходный 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]

Это не синтаксический анализ 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>
Другие вопросы по тегам