我有一个巨大的XML文件,我想根据产品类型属性拆分成块。
我不知道如何使用XSLT。我发现xml_split
但无法弄清楚如何使用正则表达式或XPath来根据类型属性拆分文档
1*500+2*400+1*200=1500
我使用了这个命令
<?xml version="1.0"?>
<!DOCTYPE catalog SYSTEM "catalog.dtd">
<catalog>
<product type="cloths" product_image="cardigan.jpg">
<catalog_item gender="Men's">
<item_number>QWZ5671</item_number>
<price>39.95</price>
<size description="Medium">
<color_swatch image="red_cardigan.jpg">Red</color_swatch>
<color_swatch image="burgundy_cardigan.jpg">Burgundy</color_swatch>
</size>
<size description="Large">
<color_swatch image="red_cardigan.jpg">Red</color_swatch>
<color_swatch image="burgundy_cardigan.jpg">Burgundy</color_swatch>
</size>
</catalog_item>
<catalog_item gender="Women's">
<item_number>RRX9856</item_number>
<price>42.50</price>
<size description="Small">
<color_swatch image="red_cardigan.jpg">Red</color_swatch>
<color_swatch image="navy_cardigan.jpg">Navy</color_swatch>
<color_swatch image="burgundy_cardigan.jpg">Burgundy</color_swatch>
</size>
<size description="Medium">
<color_swatch image="red_cardigan.jpg">Red</color_swatch>
<color_swatch image="navy_cardigan.jpg">Navy</color_swatch>
<color_swatch image="burgundy_cardigan.jpg">Burgundy</color_swatch>
<color_swatch image="black_cardigan.jpg">Black</color_swatch>
</size>
<size description="Large">
<color_swatch image="navy_cardigan.jpg">Navy</color_swatch>
<color_swatch image="black_cardigan.jpg">Black</color_swatch>
</size>
<size description="Extra Large">
<color_swatch image="burgundy_cardigan.jpg">Burgundy</color_swatch>
<color_swatch image="black_cardigan.jpg">Black</color_swatch>
</size>
</catalog_item>
</product>
</catalog>
但它会在没有XPath过滤的情况下重现完整的XML文档。
答案 0 :(得分:2)
好的,所以 - 如果我看对了你,你就是在考虑将你的产品类型分成不同的文件。
我可能会这样做,使用XML::Twig
:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
sub split_product {
my ( $twig, $product ) = @_;
open( my $output, '>>', $product->att('type') . ".xml" ) or warn $!;
print {$output} $product->sprint;
$twig -> purge;
}
my $twig = XML::Twig->new(
pretty_print => 'indented_a',
twig_handlers => { 'product' => \&split_product }
);
$twig->parsefile('source.xml');
虽然这不会保留XML结构,但它只是放置了产品&#39;元素到一个新文件。 (如果有相同类型的多个产品,那么它将不再是有效的XML。)
好的,所以给定每种类型的多个产品,遍历文件是必要的。这使它变得更加复杂,因为你无法关闭&#39;你的XML,直到你知道它需要什么,这意味着你需要两次遍历你的树。
解决此问题的更简单(内存密集)方式是:
#!/usr/bin/env perl
use strict;
use warnings;
my %products;
use XML::Twig;
sub split_product {
my ( $twig, $product ) = @_;
my $type = $product->att('type');
if ( not $products{$type} ) {
my $new_product = XML::Twig->new;
$new_product->set_root( XML::Twig::Elt->new('catalogue') );
$new_product->set_xml_version('1.0');
$new_product->set_encoding('utf-8');
$new_product->set_doctype('catalog SYSTEM "catalog.dtd"');
$products{$type} = $new_product;
}
$product->cut;
$product->paste( 'last_child', $products{$type}->root );
$twig->purge;
}
my $twig = XML::Twig->new(
pretty_print => 'indented_a',
twig_handlers => { 'product' => \&split_product }
);
$twig->parsefile ( 'your_file.xml' );
foreach my $product_type ( keys %products ) {
open ( my $output, '>', "$product_type.xml" ) or warn $!;
print {$output} $products{$product_type}->sprint;
}
这会将其分解为单独的有效文档,但要注意 - 它将占用内存中XML大小的10倍。
最后,但并非最不重要 - 一个(希望!)内存密集度较低的版本,使用flush
和purge
来转储已解析的XML。
#!/usr/bin/env perl
use strict;
use warnings;
my %products;
my %product_files;
use XML::Twig;
sub split_product {
my ( $twig, $product ) = @_;
my $type = $product->att('type');
if ( not $products{$type} ) {
my $new_product = XML::Twig->new;
$new_product->set_root( XML::Twig::Elt->new('catalogue') );
$new_product->set_xml_version('1.0');
$new_product->set_encoding('utf-8');
$new_product->set_doctype('catalog SYSTEM "catalog.dtd"');
$products{$type} = $new_product;
open( $product_files{$type}, '>', "$type.xml" ) or warn $!;
}
$product->cut;
$product->paste( 'last_child', $products{$type}->root );
$twig->purge;
$products{$type}->flush( $product_files{$type} );
}
my $twig = XML::Twig->new(
pretty_print => 'indented_a',
twig_handlers => { 'product' => \&split_product }
);
$twig->parsefile ( 'your_file.xml' );
foreach my $product_type ( keys %products ) {
$products{$product_type}->flush( $product_files{$product_type} );
close( $product_files{$product_type} );
}
如果您只想选择一种特定类型,我们可以在脚本中设置它:
my $target_type = 'cloths';
或者从@ARGV
(命令行参数)中读取它。
my ( $target_type ) = @ARGV;
然后设置你的twig_handler&#39;到:
"product[\@type=\"$target_type\"]" => \&split_product
虽然这意味着从内存中清除数据的频率较低。所以你可以添加到处理程序中:
if ( $product -> att('type') eq $target_type ) {
$twig -> purge;
return;
}