Я использую модуль perl XML::LibXML для управления XML-файлом.
Я хочу удалить открывающий и закрывающий теги узла XML, если он имеет определенный атрибут, сделав его текст и подузлы частью родительского узла.
Вот неудачная попытка. Если не получается с insertBefore/insertAfter: HIERARCHY_REQUEST_ERR:
#!/usr/bin/env perl
use 5.020;
use warnings;
use XML::LibXML;
#the input xml
my $inputstr = <<XML;
<root>
<a>
<b class = "deletethistag">keep this text<c>keep this c node</c>keep this text too</b>
<b class = "someothertag">don't change this</b>
<b>don't change this node without an attribute</b>
<c class = "type1">don't change this either</c>
</a>
</root>
XML
my $desiredstr = <<XML ;
<root>
<a>keep this text<c>keep this c node</c>keep this text too
<b class = "someothertag">don't change this</b>
<b>don't change this node without an attribute</b>
<c class = "type1">don't change this either</c>
</a>
</root>
XML
my $dom = XML::LibXML->load_xml(
string => $inputstr
);
# Convert $inputstr to $desiredstr *** doesn't work ***
foreach my $node ($dom->findnodes(q#//a/b[@class = "deletethistag"]/*#)) {
my $nodestring = $node->toString(1);
say STDERR $nodestring;
my $replacementnode = XML::LibXML->load_xml(string => $nodestring);
$node->parentNode()->insertAfter($replacementnode, $node);
$node->unbindNode();
}
say $dom->toString(1);
Я хочу использовать код для удаления разметки <span lang = "en" xml:space = "preserve">...</span> из файла, но я сформулировал его как более общий вопрос, чтобы лучше понять детали работы с XML::LibXML.





$node->childNodes() возвращает все текстовые узлы и другие подузлы $node.
Вставьте всех дочерних элементов $node в родительский элемент $node в том же месте, где находится $node. Затем удалите исходный $node с помощью $node->unbindNode().
Вот рабочий скрипт:
#!/usr/bin/env perl
use 5.020;
use warnings;
use XML::LibXML;
#the input xml
my $inputstr = <<XML;
<root>
<a>
<b class = "deletethistag">keep this text<c>keep this c node</c>keep this text too</b>
<b class = "someothertag">don't change this</b>
<b>don't change this node without an attribute</b>
<c class = "type1">don't change this either</c>
</a>
</root>
XML
my $desiredstr = <<XML ;
<root>
<a>
keep this text<c>keep this c node</c>keep this text too
<b class = "someothertag">don't change this</b>
<b>don't change this node without an attribute</b>
<c class = "type1">don't change this either</c>
</a>
</root>
XML
my $dom = XML::LibXML->load_xml(
string => $inputstr
);
for my $node ($dom->findnodes(q#//a/b[@class = "deletethistag"]#)) {
my $parent = $node->parentNode();
for my $child_node ( $node->childNodes() ) {
$parent->insertBefore($child_node, $node);
}
$node->unbindNode();
}
say $dom->toString();
Х/Т: https://stackoverflow.com/a/31680169/22989509
Обратите внимание, что я поставил перевод строки после <a> в желаемом выводе, чтобы он соответствовал вводу.
Использование
load_xmlв цикле не имеет смысла и является источником проблемы. Узел не может принадлежать двум разным документам, а документ не может принадлежать одному документу.