Создайте два отдельных файла 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>