Skip to content

Commit 054d207

Browse files
committed
Fix duplicate responses in XPath following, following-sibling, preceding, preceding-sibling
## Why? See: #251 (comment) - XPath : a/d/preceding::* => ["d", "c", "b"] ```xml <a> <b/> <!-- a/d/preceding::b --> <c/> <!-- a/d/preceding::c --> <d/> <!-- a/d/preceding::d --> <d/> <!-- self --> <e/> <f/> </a> ``` - XPath : a/d/following::* => ["d", "e", "f"] ```xml <a> <b/> <c/> <d/> <!-- self --> <d/> <!-- a/d/following::d --> <e/> <!-- a/d/following::e --> <f/> <!-- a/d/following::f --> </a> ``` - XPath : a/b/x/following-sibling:* => ["c", "d", "e"] ```xml <a> <b> <x/> <!-- self --> <c/> <!-- a/b/x/following-sibling::c --> <d/> <!-- a/b/x/following-sibling::d --> </b> <b> <x/> <!-- self --> <e/> <!-- a/b/x/following-sibling::e --> </b> </a> ``` - XPath : a/b/x/following-sibling:* => ["c", "d", "x", "e"] ```xml <a> <b> <x/> <!-- self --> <c/> <!-- a/b/x/following-sibling::c --> <d/> <!-- a/b/x/following-sibling::d --> <x/> <!-- a/b/x/following-sibling::x --> <e/> <!-- a/b/x/following-sibling::e --> </b> </a> ``` - XPath : a/b/x/preceding-sibling::* => ["e", "d", "c"] ```xml <a> <b> <c/> <!-- a/b/x/preceding-sibling::c --> <d/> <!-- a/b/x/preceding-sibling::d --> <x/> <!-- self --> </b> <b> <e/> <!-- a/b/x/preceding-sibling::e --> <x/> <!-- self --> </b> </a> ``` - XPath : a/b/x/preceding-sibling::* => ["e", "x", "d", "c"] ```xml <a> <b> <c/> <!-- a/b/x/preceding-sibling::c --> <d/> <!-- a/b/x/preceding-sibling::d --> <x/> <!-- a/b/x/preceding-sibling::x --> <e/> <!-- a/b/x/preceding-sibling::e --> <x/> <!-- self --> </b> </a> ``` - XPath : //a/following-sibling:*[1] => ["w", "x", "y", "z"] ```xml <div> <div> <a/> <-- self --> <w/> <-- //a/following-sibling:*[1] --> </div> <a/> <-- self --> <x/> <-- //a/following-sibling:*[1] --> <a/> <-- self --> <y/> <-- //a/following-sibling:*[1] --> <a/> <-- self --> <z/> <-- //a/following-sibling:*[1] --> </div> ```
1 parent de6f40e commit 054d207

File tree

2 files changed

+95
-4
lines changed

2 files changed

+95
-4
lines changed

lib/rexml/xpath_parser.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ def match(path_stack, nodeset)
144144
result = expr(path_stack, nodeset)
145145
case result
146146
when Array # nodeset
147-
unnode(result)
147+
unnode(result).uniq
148148
else
149149
[result]
150150
end

test/xpath/test_base.rb

+94-3
Original file line numberDiff line numberDiff line change
@@ -416,12 +416,103 @@ def test_preceding
416416
assert_equal( 4, cs.length )
417417
end
418418

419-
def test_preceding_sibling
420-
d = REXML::Document.new("<a><b><c/><d/><x/></b><b><e/><x/></b></a>")
421-
matches = REXML::XPath.match(d, "a/b/x/preceding-sibling::node()")
419+
def test_preceding_multiple
420+
source = <<-XML
421+
<a>
422+
<b/><c/><d/><d/><e/><f/>
423+
</a>
424+
XML
425+
doc = REXML::Document.new(source)
426+
matches = REXML::XPath.match(doc, "a/d/preceding::*")
427+
assert_equal(["d", "c", "b"], matches.map(&:name))
428+
end
429+
430+
def test_following_multiple
431+
source = <<-XML
432+
<a>
433+
<b/><c/><d/><d/><e/><f/>
434+
</a>
435+
XML
436+
doc = REXML::Document.new(source)
437+
matches = REXML::XPath.match(doc, "a/d/following::*")
438+
assert_equal(["d", "e", "f"], matches.map(&:name))
439+
end
440+
441+
def test_following_sibling_across_multiple_nodes
442+
source = <<-XML
443+
<a>
444+
<b>
445+
<x/><c/><d/>
446+
</b>
447+
<b>
448+
<x/><e/>
449+
</b>
450+
</a>
451+
XML
452+
doc = REXML::Document.new(source)
453+
matches = REXML::XPath.match(doc, "a/b/x/following-sibling::*")
454+
assert_equal(["c", "d", "e"], matches.map(&:name))
455+
end
456+
457+
def test_following_sibling_within_single_node
458+
source = <<-XML
459+
<a>
460+
<b>
461+
<x/><c/><d/><x/><e/>
462+
</b>
463+
</a>
464+
XML
465+
doc = REXML::Document.new(source)
466+
matches = REXML::XPath.match(doc, "a/b/x/following-sibling::*")
467+
assert_equal(["c", "d", "x", "e"], matches.map(&:name))
468+
end
469+
470+
def test_following_sibling_predicates
471+
source = <<-XML
472+
<div>
473+
<div>
474+
<a/><w/>
475+
</div>
476+
<a/><x/>
477+
<a/><y/>
478+
<a/><z/>
479+
</div>
480+
XML
481+
doc = REXML::Document.new(source)
482+
# Finds a node flowing <a/>
483+
matches = REXML::XPath.match(doc, "//a/following-sibling::*[1]")
484+
assert_equal(["w", "x", "y", "z"], matches.map(&:name))
485+
end
486+
487+
def test_preceding_sibling_across_multiple_nodes
488+
source = <<-XML
489+
<a>
490+
<b>
491+
<c/><d/><x/>
492+
</b>
493+
<b>
494+
<e/><x/>
495+
</b>
496+
</a>
497+
XML
498+
doc = REXML::Document.new(source)
499+
matches = REXML::XPath.match(doc, "a/b/x/preceding-sibling::*")
422500
assert_equal(["e", "d", "c"], matches.map(&:name))
423501
end
424502

503+
def test_preceding_sibling_within_single_node
504+
source = <<-XML
505+
<a>
506+
<b>
507+
<c/><d/><x/><e/><x/>
508+
</b>
509+
</a>
510+
XML
511+
doc = REXML::Document.new(source)
512+
matches = REXML::XPath.match(doc, "a/b/x/preceding-sibling::*")
513+
assert_equal(["e", "x", "d", "c"], matches.map(&:name))
514+
end
515+
425516
def test_following
426517
d = Document.new "<a><b id='0'/><b/><b><c id='1'/><c id='2'/></b><b id='1'/></a>"
427518
start = XPath.first( d, "/a/b[@id='0']" )

0 commit comments

Comments
 (0)