尝试使用 XML::Twig 插入 XML sub-tree 作为新元素时出现问题
Issue when trying to insert an XML sub-tree as new element using XML::Twig
(这是 的 XML 变体)
为我的 object 创建一个 XML 演示文稿,我将 sub-objects 的 XML 的创建推迟到那些。
在初始版本工作后,但创建了一个不必要的深度嵌套结构,我试图将结构稍微扁平化,但是 运行 变成了一个我只能通过一些丑陋的 work-around 解决的问题。
让我们看一下这段代码(在 foreach
循环中执行):
my $child = XML::Twig::Elt->new('sample', { 'name' => $_ });
my $F = $val->{$_}->XML_string($child);
$F->print;
$child = $elt->insert_new_elt(last_child => 'dummy');
$child->replace_with($F);
$F
(分配给临时变量用于调试目的)包含 XML sub-tree 用于 object $val->{$_}
($val
是当前正在处理的 object 插槽,在本例中它是 sub-objects 的集合(散列)。
所以 $val->{$_}
是要处理的 sub-object)。
sub-tree 添加为额外参数的 children($child
,即:sample
)。
我想做的是 $elt->insert_new_elt(last_child => $F);
,但这从来没有奏效。
所以我在正确的位置插入了一个dummy
元素,只是为了之后替换它。
下面是这些行的一些调试输出:
### The parent node where the new 'sample' children should be added
DB<4> p $elt->print
<perf_data/>
### The new child (it's still too complex...)
DB<5> p $F->print
<sample name="max"><label>max</label><value>2.48584</value><unit/><warn><range part="end">0.5</range><range part="inverted">0</range><range part="start">0</range></warn><crit><range part="end">1</range><range part="inverted">0</range><range part="start">0</range></crit><min>0</min><max/></sample>
### the dummy child
DB<6> p $child->print
<dummy/>
### The parent after adding the dummy child
DB<7> p $elt->print
<perf_data><dummy/></perf_data>
### the parent after having replaced the dummy child with the real one
DB<8> p $elt->print
<perf_data><sample name="max"><label>max</label><value>2.48584</value><unit/><warn><range part="end">0.5</range><range part="inverted">0</range><range part="start">0</range></warn><crit><range part="end">1</range><range part="inverted">0</range><range part="start">0</range></crit><min>0</min><max/></sample></perf_data>
### more 'sample' elements to follow...
如何避免临时 dummy
元素?
我试图从文档中找到 pout,但失败了。
也许要添加 children in-order,我应该使用 insert_new_elt
以外的东西(也许是 append_new_elt
?),但大多数文档都是关于解析现有的 XML, 不构造 XML.
简化替代版本
由于我被要求提供一个简化版本,这里是一个。
然而,它与原始数据结构只有一点点相似。
至少我试图保留问题的本质。
代码如下:
#!/usr/bin/perl
use strict;
use warnings;
use 5.018;
package FOO;
use constant ATTRIBUTES => (
['a', 0],
['b', 1],
['c', 2],
['d', 3],
);
sub new($)
{
my $class = shift;
my $self = [];
$#$self = 4;
bless $self, $class;
foreach (@$self) {
$_ = int(rand(10))
}
return $self;
}
use XML::Twig;
sub XML_string($;$)
{
my ($self, $root) = @_;
$root //= XML::Twig::Elt->new(__PACKAGE__);
foreach (ATTRIBUTES) {
my ($name, $i) = @$_[0, 1];
if (defined(my $val = $self->[$i])) {
my $elt = $root->insert_new_elt(last_child => $name);
if ($i == 1 || $i == 2) {
my $range = $elt->insert_new_elt(last_child => 'range');
$range->set_att('baz' => $val);
}
} # else leave out
}
return $root;
}
package BAR;
use constant ATTRIBUTES => (
['e', 0],
['f', 1],
['g', 2],
['h', 3],
);
sub new($)
{
my $class = shift;
my $self = [];
$#$self = 4;
bless $self, $class;
foreach (@$self) {
$_ = int(rand(10))
}
return $self;
}
use XML::Twig;
sub XML_string($;$)
{
my $self = shift;
my $xml = XML::Twig->new()->set_xml_version('1.0')->set_encoding('utf-8');
my $b = XML::Twig::Elt->new(__PACKAGE__, {'version' => '1.0'});
$xml->set_root($b);
foreach (ATTRIBUTES) {
my ($name, $i) = @$_[0, 1];
if (defined(my $val = $self->[$i])) {
my $elt = $b->insert_new_elt(last_child => $name);
if ($i == 1) {
my $e = $elt->insert_new_elt(last_child => 'sample');
my $F = $val->XML_string($e);
#print $F->print,"\n";;
$e = $elt->insert_new_elt(last_child => 'dummy');
$e->replace_with($F);
}
} # else leave out
}
if ($#_ >= 0 && $_[0]) {
$xml->set_pretty_print('indented');
}
return $xml->sprint();
}
package main;
my $f1 = FOO->new();
my $f2 = FOO->new();
my $b = BAR->new();
$b->[1] = $f1;
$b->[2] = $f2;
print $b->XML_string(1), "\n";
示例输出可能如下所示:
DB<3> x $b
0 BAR=ARRAY(0x1b83860)
0 1
1 FOO=ARRAY(0xe2ad40)
0 2
1 4
2 0
3 4
4 7
2 FOO=ARRAY(0x1633788)
0 3
1 8
2 0
3 1
4 0
3 3
4 1
DB<4> n
<?xml version="1.0" encoding="utf-8"?>
<BAR version="1.0">
<e/>
<f>
<sample>
<a/>
<b>
<range baz="4"/>
</b>
<c>
<range baz="0"/>
</c>
<d/>
</sample>
</f>
<g/>
<h/>
</BAR>
我希望它与原始问题足够相似。
不要使用新元素的构造函数。 insert_new_elt
:
直接使用构造函数参数
my $child = $elt->insert_new_elt(last_child => 'sample', {name => $_});
$val->{$_}->XML_string($child);
或者,使用 paste
:
my $child = XML::Twig::Elt->new('sample', {name => $_});
$child->paste(last_child => $elt);
(这是
为我的 object 创建一个 XML 演示文稿,我将 sub-objects 的 XML 的创建推迟到那些。 在初始版本工作后,但创建了一个不必要的深度嵌套结构,我试图将结构稍微扁平化,但是 运行 变成了一个我只能通过一些丑陋的 work-around 解决的问题。
让我们看一下这段代码(在 foreach
循环中执行):
my $child = XML::Twig::Elt->new('sample', { 'name' => $_ });
my $F = $val->{$_}->XML_string($child);
$F->print;
$child = $elt->insert_new_elt(last_child => 'dummy');
$child->replace_with($F);
$F
(分配给临时变量用于调试目的)包含 XML sub-tree 用于 object $val->{$_}
($val
是当前正在处理的 object 插槽,在本例中它是 sub-objects 的集合(散列)。
所以 $val->{$_}
是要处理的 sub-object)。
sub-tree 添加为额外参数的 children($child
,即:sample
)。
我想做的是 $elt->insert_new_elt(last_child => $F);
,但这从来没有奏效。
所以我在正确的位置插入了一个dummy
元素,只是为了之后替换它。
下面是这些行的一些调试输出:
### The parent node where the new 'sample' children should be added
DB<4> p $elt->print
<perf_data/>
### The new child (it's still too complex...)
DB<5> p $F->print
<sample name="max"><label>max</label><value>2.48584</value><unit/><warn><range part="end">0.5</range><range part="inverted">0</range><range part="start">0</range></warn><crit><range part="end">1</range><range part="inverted">0</range><range part="start">0</range></crit><min>0</min><max/></sample>
### the dummy child
DB<6> p $child->print
<dummy/>
### The parent after adding the dummy child
DB<7> p $elt->print
<perf_data><dummy/></perf_data>
### the parent after having replaced the dummy child with the real one
DB<8> p $elt->print
<perf_data><sample name="max"><label>max</label><value>2.48584</value><unit/><warn><range part="end">0.5</range><range part="inverted">0</range><range part="start">0</range></warn><crit><range part="end">1</range><range part="inverted">0</range><range part="start">0</range></crit><min>0</min><max/></sample></perf_data>
### more 'sample' elements to follow...
如何避免临时 dummy
元素?
我试图从文档中找到 pout,但失败了。
也许要添加 children in-order,我应该使用 insert_new_elt
以外的东西(也许是 append_new_elt
?),但大多数文档都是关于解析现有的 XML, 不构造 XML.
简化替代版本
由于我被要求提供一个简化版本,这里是一个。 然而,它与原始数据结构只有一点点相似。 至少我试图保留问题的本质。
代码如下:
#!/usr/bin/perl
use strict;
use warnings;
use 5.018;
package FOO;
use constant ATTRIBUTES => (
['a', 0],
['b', 1],
['c', 2],
['d', 3],
);
sub new($)
{
my $class = shift;
my $self = [];
$#$self = 4;
bless $self, $class;
foreach (@$self) {
$_ = int(rand(10))
}
return $self;
}
use XML::Twig;
sub XML_string($;$)
{
my ($self, $root) = @_;
$root //= XML::Twig::Elt->new(__PACKAGE__);
foreach (ATTRIBUTES) {
my ($name, $i) = @$_[0, 1];
if (defined(my $val = $self->[$i])) {
my $elt = $root->insert_new_elt(last_child => $name);
if ($i == 1 || $i == 2) {
my $range = $elt->insert_new_elt(last_child => 'range');
$range->set_att('baz' => $val);
}
} # else leave out
}
return $root;
}
package BAR;
use constant ATTRIBUTES => (
['e', 0],
['f', 1],
['g', 2],
['h', 3],
);
sub new($)
{
my $class = shift;
my $self = [];
$#$self = 4;
bless $self, $class;
foreach (@$self) {
$_ = int(rand(10))
}
return $self;
}
use XML::Twig;
sub XML_string($;$)
{
my $self = shift;
my $xml = XML::Twig->new()->set_xml_version('1.0')->set_encoding('utf-8');
my $b = XML::Twig::Elt->new(__PACKAGE__, {'version' => '1.0'});
$xml->set_root($b);
foreach (ATTRIBUTES) {
my ($name, $i) = @$_[0, 1];
if (defined(my $val = $self->[$i])) {
my $elt = $b->insert_new_elt(last_child => $name);
if ($i == 1) {
my $e = $elt->insert_new_elt(last_child => 'sample');
my $F = $val->XML_string($e);
#print $F->print,"\n";;
$e = $elt->insert_new_elt(last_child => 'dummy');
$e->replace_with($F);
}
} # else leave out
}
if ($#_ >= 0 && $_[0]) {
$xml->set_pretty_print('indented');
}
return $xml->sprint();
}
package main;
my $f1 = FOO->new();
my $f2 = FOO->new();
my $b = BAR->new();
$b->[1] = $f1;
$b->[2] = $f2;
print $b->XML_string(1), "\n";
示例输出可能如下所示:
DB<3> x $b
0 BAR=ARRAY(0x1b83860)
0 1
1 FOO=ARRAY(0xe2ad40)
0 2
1 4
2 0
3 4
4 7
2 FOO=ARRAY(0x1633788)
0 3
1 8
2 0
3 1
4 0
3 3
4 1
DB<4> n
<?xml version="1.0" encoding="utf-8"?>
<BAR version="1.0">
<e/>
<f>
<sample>
<a/>
<b>
<range baz="4"/>
</b>
<c>
<range baz="0"/>
</c>
<d/>
</sample>
</f>
<g/>
<h/>
</BAR>
我希望它与原始问题足够相似。
不要使用新元素的构造函数。 insert_new_elt
:
my $child = $elt->insert_new_elt(last_child => 'sample', {name => $_});
$val->{$_}->XML_string($child);
或者,使用 paste
:
my $child = XML::Twig::Elt->new('sample', {name => $_});
$child->paste(last_child => $elt);