修复了一个xml文件中没有要分隔的id的多行



我有一个外部生成的大型xml文件,其中包含一些无效字符,在我的情况下是一个反斜杠。我知道用什么来替换这些字段,所以我可以将其保存在一个文件中并手动修复。然而,这些文件中有许多都存在相同的问题。我想写一个bash脚本来修复它们。

问题有问题的部分如下所示。

<root>
<array>
<dimension> dim="1">gridpoints</dimension>
<field> a </field>
<field> b </field>
<field> c </field>
<field> 000 </field>
<field> 000 </field>
<field> 000 </field>
<set> 
All the data 
</set>
</array>
</root>

所需输出

<root>
<array>
<dimension> dim="1">gridpoints</dimension>
<dimension> dim="2">morepoints</dimension>
<dimension> dim="3">evenmorepoints</dimension>
<field> a </field>
<field> b </field>
<field> c </field>
<field> d </field>
<field> e </field>
<field> f </field>
<set> 
All the data 
</set>
</array>
</root>

到目前为止修复了我已经找到了一种使用perl删除有问题的反斜杠的方法,但我不知道如何单独编辑字段,因为下面的代码得到了所需的解决方案,但每个字段都有条目";a";

#!/bin/bash
perl -CSDA -pe'
s/[^x9xAxDx20-x{D7FF}x{E000}-x{FFFD}x{10000}-x{10FFFF}]+//g;
' file.xml > temp.xml
xmlstarlet ed -u "/root/array/field" -v "a" temp.xml > file_fixed.xml

我也很乐意接受任何关于如何更有效地做到这一点的建议。非常感谢。

编辑根据zdim的要求,我添加了一个更能代表我正在处理的完整文件的示例。

<root>
<path1>
<array>
<dimension> dim="1">gridpoints</dimension>
<field> a </field>
<field> b </field>
<field> c </field>
<field> 000 </field>
<field> 000 </field>
<field> 000 </field>
<set> 
All the data 
</set>
</array>
</path1>
<path2>
<array>
<dimension> dim="1">gridpoints</dimension>
<field> Behaves Correctly </field>
</array>
</path2>
</root>

需要注意的是,我从另一个程序接收这些文件作为输出,然后需要在将它们输入下一个程序之前修复它们。我对xml没有任何经验,这就是为什么我可能错过了一些显而易见的解决方案。

使用适当的XML解析器。

使用XML::LibXML,单向

use warnings;
use strict;
use feature 'say';
use XML::LibXML;
my $filename = shift // die "Usage: $0 file.xmln";  #/ fix syntax hilite
my $doc = XML::LibXML->load_xml(location => $filename);
# Remove unwanted nodes
foreach my $node ($doc->findnodes('//field')) { 
#say $node->toString;   
if ($node->toString =~ m{\00\00\00}) {
say "Removing $node";
$node->parentNode->removeChild($node);
}   
}
# Add desired new nodes (right after the last <field> node)
my $last_field_node = ( $doc->findnodes('//field') )[-1];
my $field_node_name = $last_field_node->nodeName;
my $parent = $last_field_node->parentNode;
for ("E".."F") {
my $new_elem = $doc->createElement( $field_node_name );
$new_elem->appendText($_);
$parent->insertAfter($new_elem, $last_field_node);
}
# Add other nodes (like the mentioned "dimension") the same way
print $doc->toString;

我使用一个基本的正则表达式来识别要删除的模式,如示例中所示。请根据您的实际输入调整代码。

这会在最后一个<field>节点之后添加新节点。但是,如果我们需要在删除的节点之后立即添加,而可能还有更多的<field>节点,那么首先在需要删除的最后一个<field>节点之后添加,然后再删除它们。

或者,您可能只需要将<field>节点的内容替换为'000'

my @replacements = "AA" .. "ZZ";  # li'l list of token replacements 
foreach my $node ($doc->findnodes('//field')) { 
if ($node->toString =~ m{\00\00\00}) {
say "Change $node -- remove child (text) nodes, add new";
$node->removeChildNodes;
$node->appendText(shift @replacements);
}
}

元素的";值";实际上是一个文本节点,它有一个值。与其直接替换(文本子节点的(值,不如删除(所有(元素的(文本(子节点,然后添加所需的新节点。

如果需要简单地替换000,则此代码将根据替换列表进行处理。若要添加<dimension>节点,请如上所述使用insertAfter

有一些模块可以进行更漂亮的打印,如XML::LibXML::PrettyPrint


使用Mojo::DOM,单向

use warnings;
use strict;
use feature 'say';
use Path::Tiny;  # convenience, for "slurp"-ing a file
use Mojo::DOM;
my $filename = shift // die "Usage: $0 file.xmln";  #/ fix syntax hilite
my $dom = Mojo::DOM->new( path($filename)->slurp );
# my $dom = Mojo::DOM->new->xml(1)->parse(path($filename)->slurp);
# Remove unwanted, by filtering them first
$dom->find("field")
-> grep( sub { $_->text =~ m{\00\00\00} } )
-> each( sub { $_[0]->remove } );
# Or directly while iterating
# $dom->find("field")->each(
#     sub { $_[0]->remove if $_[0]->text =~ m{\00} } );
# Add new ones, after last 'field'
foreach my $content ("E".."F") {
my $tag = $dom->new_tag('field', $content);
$dom->find('field')->last->append($tag);
}
say $dom;

再次,请根据实际文档结构进行调整。

一个例子。如果需要在要删除的field节点之后添加新的field节点(而不是在其他一些field节点之后(,一种方法是首先在这些节点之后添加,而我们仍然可以识别这些位置,然后再删除它们。

# Add new ones, after last 'field' that has 000 text in it
foreach my $content ("E".."F") {
my $tag = $dom->new_tag('field', $content);
$dom->find('field')->grep(sub { m{\00\00\00} })->last->append($tag);
}
# Only now remove those 'field' nodes with 000
$dom->find("field")->each( 
sub { $_[0]->remove if $_[0] =~ m{\00\00\00} } );

有了这个库,如果需要的,也可以很容易地替换节点的内容(而不是添加和删除(。

最新更新