我有一个很大的(有几十万条记录)XML 文件,我想从中仅过滤特定字段。文件构造示例:
<A>
<id>123</id>
<B>
<C>value1</C>
<D>value2</D>
....
<E></E>
</B>
<Z></Z>
...
<Y></Y>
<A>
我想过滤此 XML 文件并仅包含 C 和 D 字段中包含的 id 和数据。
如何做到这一点?
答案1
这xmlstarlet工具将执行以下操作:
xmlstarlet sel -t -m /A -o ID, -v id -n -o C, -v //C -n -o D, -v //D -n test.xml
对于根元素 ( -m /A
) 下的每个 A,它打印字符串“ID”( -o ID,
)、id ( ) 的内容-v id
、换行符 ( -n
),对于子元素 C ( -v //C
) 和 D ( -v //D
) 及其各自的标头也是如此。双斜杠是“匹配节点下的任何位置”的 XPath。
使用您的测试文件在我的系统上进行测试的结果是逗号分隔的输出:
ID,123
C,value1
D,value2
如果您不需要标头,请-o <whatever>
省略参数。
谢谢本文进行解释。
答案2
为了正确回答这个问题,我们理想地需要一个更好的例子 - 一些有效的xml 是一个好的开始。
另外 - 所需输出的示例。例如,您不需要指明您希望<C>
和<D>
元素在生成的 XML 中结束的位置。他们是已经的子级<B>
- 您想要保留B
还是重新设置父级C
以及D
根?
然而,一般来说,使用和进行重建XML
非常容易。XML::Twig
perl
例如像这样:
#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig;
my @wanted = qw ( C D id );
my %wanted = map { $_ => 1 } @wanted;
sub delete_unwanted_tags {
my ( $twig, $element ) = @_;
my $tag = $element -> tag;
if ( not $wanted{$tag} ) {
$element -> delete;
}
}
my $twig = XML::Twig -> new ( twig_handlers => { _all_ => \&delete_unwanted_tags } );
$twig -> parse ( \*DATA );
$twig -> print;
__DATA__
<A>
<id>123</id>
<B>
<C>value1</C>
<D>value2</D>
<E></E>
</B>
<Z></Z>
<Y></Y>
</A>
因为我们没有说“保留<B>
”,所以结果是:
<A>
<id>123</id>
</A>
添加<B>
到wanted
列表:
<A>
<id>123</id>
<B>
<C>value1</C>
<D>value2</D>
</B>
</A>
但是,如果您想要做的是 reparentC
并D
进入A
:
#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig;
my @wanted = qw ( id);
my @reparent = qw ( C D );
#turn the above into hashes, so we can do "if $wanted{$tag}"
my %wanted = map { $_ => 1 } @wanted;
my %reparent = map { $_ => 1 } @reparent;
sub delete_unwanted_tags {
my ( $twig, $element ) = @_;
my $tag = $element->tag;
if ( not $wanted{$tag} ) {
$element->delete;
}
if ( $reparent{$tag} ) {
$element->move( 'last_child', $twig->root );
}
}
my $twig = XML::Twig->new(
pretty_print => 'indented_a',
twig_handlers => { _all_ => \&delete_unwanted_tags }
);
$twig->parse( \*DATA );
$twig->print;
__DATA__
<A>
<id>123</id>
<B>
<C>value1</C>
<D>value2</D>
<E></E>
</B>
<Z></Z>
<Y></Y>
</A>
注意 - “twig handler”在结尾每个元素的值(当遇到关闭标记时),这就是为什么它有效 - 我们在完成处理(和删除)之前递归查找C
和。D
B
这会产生:
<A>
<id>123</id>
<C>value1</C>
<D>value2</D>
</A>
在上面,我使用了__DATA__
,\*DATA
和 ,parse
因为它允许我说明 XML 和技术。您可能应该使用parsefile('my_file.xml')
而不是parse(\*DATA)
.
答案3
使用 ltXML2 工具包(爱丁堡大学)中的 lxgrep,例如
$ lxgrep -w A '(id|C|D)' test.xml
<A>
<id>123</id>
<C>value1</C>
<D>value2</D>
</A>
使用这些类型的工具是远的比自己动手更快、更可靠。
XML 常见问题解答:http://xml.silmaril.ie/