Skip Menu |

This queue is for tickets about the XML-XPath CPAN distribution.

Report information
The Basics
Id: 26144
Status: resolved
Priority: 0/
Queue: XML-XPath

People
Owner: MANWAR [...] cpan.org
Requestors: charleswoerner [...] gmail.com
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: 1.28
Fixed in: 1.29



Subject: createNode doesn't honor position() predicate
I wan't to be able to say "/foo/bar/baz[3]" and have createNode create the following xml frag... <foo><bar><baz /><baz /><baz /></bar></foo> ...even if foo/bar/baz does not exist. Right now XML::XPath drops the predicate and effectively creates foo/bar/baz[1]. That was not what I expected.
From: charleswoerner [...] gmail.com
The attached patch and testcase provide this functionality. On Fri Apr 06 21:16:21 2007, cwoerner wrote: Show quoted text
> I wan't to be able to say "/foo/bar/baz[3]" and have createNode create > the following xml frag... > > <foo><bar><baz /><baz /><baz /></bar></foo> > > ...even if foo/bar/baz does not exist. Right now XML::XPath drops the > predicate and effectively creates foo/bar/baz[1]. That was not what I > expected.
package main; use strict; use warnings; use Test::More tests => 5; use_ok("XML::XPath"); my $path = XML::XPath->new(ioref => \*DATA); $path->createNode("/child::foo/child::bar/child::baz"); $path->setNodeText("/child::foo/child::bar/child::baz[position()=last()]", "blah"); $path->setNodeText("/child::foo/child::bar/child::baz[position()=last()]/attribute::id", "id0"); $path->createNode("/child::foo/child::bar/child::baz[position()=3]"); $path->setNodeText("/child::foo/child::bar/child::baz[position()=last()]", "blah 2"); $path->setNodeText("/child::foo/child::bar/child::baz[position()=last()]/\@id", "id1"); my $set = $path->find("/foo/bar/baz"); my @nodelist = $set->get_nodelist; ok($nodelist[0]->toString =~ /id="id0"/); ok($nodelist[1]->toString !~ /id/); ok($nodelist[2]->toString =~ /id="id1"/); $path->createNode("/child::foo/child::bar/child::baz[5]"); $set = $path->find("/foo/bar/baz"); @nodelist = $set->get_nodelist; is(scalar(@nodelist), 5); __DATA__ <?xml version="1.0" ?> <instanceData> </instanceData>
--- /usr/local/lib/perl5/site_perl/5.8.6/XML/XPath.pm 2003-01-26 11:33:17.000000000 -0800 +++ lib/XML/XPath.pm 2007-04-06 15:34:12.000000000 -0700 @@ -225,50 +225,68 @@ my($node_path) = @_; my $path_steps = $self->{path_parser}->parse($node_path); my @path_steps = (); - foreach my $step (@{$path_steps->get_lhs()}) { + my (undef, @path_steps_lhs) = @{$path_steps->get_lhs()}; + foreach my $step (@path_steps_lhs) { # precompute paths as string my $string = $step->as_string(); - push(@path_steps, $string) if (defined $string && $string ne ""); + push(@path_steps, $string); } my $prev_node = undef; my $nodeset = undef; my $nodes = undef; my $p = undef; my $test_path = ""; + # Start with the deepest node, working up the path (right to left), # trying to find a node that exists. - for ($p = $#path_steps; $p >= 0; $p--) { - my $path = $path_steps[$p]; + + for ($p = $#path_steps_lhs; $p >= 0; $p--) { + my $path = $path_steps_lhs[$p]; $test_path = "(/" . join("/", @path_steps[0..$p]) . ")"; + $nodeset = $self->findnodes($test_path); return undef if (!defined $nodeset); # error looking for node $nodes = $nodeset->size; return undef if ($nodes > 1); # too many paths - path not specific enough if ($nodes == 1) { # found a node -- need to create nodes below it - $prev_node = $nodeset->get_node(1); - last; + $prev_node = $nodeset->get_node(1); + last; } } if (!defined $prev_node) { my @root_nodes = $self->findnodes('/')->get_nodelist(); $prev_node = $root_nodes[0]; } + # We found a node that exists, or we'll start at the root. # Create all lower nodes working left to right along the path. - for ($p++ ; $p <= $#path_steps; $p++) { - my $path = $path_steps[$p]; + + for ($p++ ; $p <= $#path_steps_lhs; $p++) { + my $path = $path_steps_lhs[$p]; my $newnode = undef; - my($axis,$name) = ($path =~ /^(.*?)::(.*)$/); - if ($axis =~ /^child$/i) { - $newnode = XML::XPath::Node::Element->new($name); - return undef if (!defined $newnode); # could not create new node - $prev_node->appendChild($newnode); - } elsif ($axis =~ /^attribute$/i) { - $newnode = XML::XPath::Node::Attribute->new($name, ""); - return undef if (!defined $newnode); # could not create new node - $prev_node->appendAttribute($newnode); - } - $prev_node = $newnode; + my $axis = $path->{axis}; + my $name = $path->{literal}; + + do { + if ($axis =~ /^child$/i) { + $newnode = XML::XPath::Node::Element->new($name); + return undef if (!defined $newnode); # could not create new node + $prev_node->appendChild($newnode); + } elsif ($axis =~ /^attribute$/i) { + $newnode = XML::XPath::Node::Attribute->new($name, ""); + return undef if (!defined $newnode); # could not create new node + $prev_node->appendAttribute($newnode); + } + #$prev_node = $newnode; + + $test_path = "(/" . join("/", @path_steps[0..$p]) . ")"; + $nodeset = $self->findnodes($test_path); + $nodes = $nodeset->size; + die "failed to find node '$test_path'" if (!defined $nodeset); # error looking for node + } while ($nodes < 1); + + $prev_node = $nodeset->get_node(1); } + return $prev_node; }
From: charleswoerner [...] gmail.com
The last post should have read that the patch provides support for the following equivalent syntaxes... $path->createNode("/foo/bar/baz[position()=3]"); $path->createNode("/foo/bar/baz[3]");
Hi, Thanks for reporting the issue. I will try to get it patched asap. Much appreciated for providing patch and test script. Best Regards, Mohammad S Anwar
Resolved.