Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fix reverse sort in xpath_parser #251

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 3, 2025
Merged

Conversation

tompng
Copy link
Member

@tompng tompng commented Apr 30, 2025

The code below was failing with REXML::XPathParser#sort': undefined method '-@' for an instance of Array

d = REXML::Document.new("<a><b><c/><d/><x/></b><b><e/><x/></b></a>")
matches = REXML::XPath.match(d, "a/b/x/preceding-sibling::node()")
# Before: error
# After: [<e/>, <d/>, <c/>]

This pull request will fix it.

@naitoh
Copy link
Contributor

naitoh commented May 1, 2025

Thanks.
However, there may be other problems, since more <d/> tags would increase the number of <c/> and <b/> tags.

After

$ irb
> $LOAD_PATH.unshift(File.expand_path("lib"))
> require 'rexml'
> REXML::XPath.match(REXML::Document.new("<a><b/><c/><d/></a>"), "/a/d/preceding::node()")
=> [<c/>, <b/>]
> REXML::XPath.match(REXML::Document.new("<a><b/><c/><d/><d/></a>"), "/a/d/preceding::node()")
=> [<d/>, <c/>, <c/>, <b/>, <b/>]
> REXML::XPath.match(REXML::Document.new("<a><b/><c/><d/><d/><d/></a>"), "/a/d/preceding::node()")
=> [<d/>, <d/>, <d/>, <c/>, <c/>, <c/>, <b/>, <b/>, <b/>]
> REXML::XPath.match(REXML::Document.new("<a><b/><c/><d/><d/><d/><d/></a>"), "/a/d/preceding::node()")
=> [<d/>, <d/>, <d/>, <d/>, <d/>, <d/>, <c/>, <c/>, <c/>, <c/>, <b/>, <b/>, <b/>, <b/>]

@tompng
Copy link
Member Author

tompng commented May 1, 2025

I think the duplicated match result is not related to this change.
To avoid adding a test case of questionable duplicated match result, I changed the test to use preceding-sibling.

d = REXML::Document.new("<a><b><c/><d/><x/></b><b><e/><x/></b></a>")
matches = REXML::XPath.match(d, "a/b/x/preceding-sibling::node()")
# Before: REXML::XPathParser#sort': undefined method '-@' for an instance of Array
# After: [<e/>, <d/>, <c/>]

@naitoh
Copy link
Contributor

naitoh commented May 2, 2025

I think the duplicated match result is not related to this change.

I agree.
Thanks.

To avoid adding a test case of questionable duplicated match result, I changed the test to use preceding-sibling.

But, the response order is reversed from nokogiri and libxml, python(lxml).

@kou
Do you think the rexml responses is in the correct order? 🤔

$ irb -rnokogiri
> puts Nokogiri::HTML("<a><b><c/><d/><x/></b><b><e/><x/></b></a>").xpath( "//a/b/x/preceding-sibling::node()")
<c></c>
<d></d>
<e></e>
$ irb -rlibxml
> puts LibXML::XML::Document.string("<a><b><c/><d/><x/></b><b><e/><x/></b></a>").find("//a/b/x/preceding-sibling::node()").to_a
<c/>
<d/>
<e/>

https://programiz.pro/ide/python

from lxml import etree
print(etree.fromstring("<a><b><c/><d/><x/></b><b><e/><x/></b></a>").xpath('//a/b/x/preceding-sibling::node()'))

[<Element c at 0x7887f2cc7580>, <Element d at 0x7887f2cc75c0>, <Element e at 0x7887f2cc7600>]

https://scrapinghub.github.io/xpath-playground/

2025-05-02 8 38 51

https://www.w3.org/TR/2017/REC-xpath-31-20170321/#doc-xpath31-ReverseAxis

the preceding-sibling axis contains the context node's preceding siblings, those children of the context node's parent that occur before the context node in document order;

I don't think it specifically states that the order should be reversed.

@kou
Copy link
Member

kou commented May 2, 2025

But, the response order is reversed from nokogiri and libxml, python(lxml).

All of them are bindings of libxml2. So it's natural that all of them have the same behavior. :-)

Do you think the rexml responses is in the correct order? 🤔

Yes.

match returns a "node-set" that is unordered.

https://www.w3.org/TR/1999/REC-xpath-19991116/#section-Introduction

node-set (an unordered collection of nodes without duplicates)

FYI: Most implementations use XPath 1.0 not XPath 2.0, 3.1, ... REXML also uses XPath 1.0.

@naitoh
Copy link
Contributor

naitoh commented May 2, 2025

But, the response order is reversed from nokogiri and libxml, python(lxml).

All of them are bindings of libxml2. So it's natural that all of them have the same behavior. :-)

Oh!
I see.

Do you think the rexml responses is in the correct order? 🤔

Yes.

match returns a "node-set" that is unordered.

https://www.w3.org/TR/1999/REC-xpath-19991116/#section-Introduction

node-set (an unordered collection of nodes without duplicates)

FYI: Most implementations use XPath 1.0 not XPath 2.0, 3.1, ... REXML also uses XPath 1.0.

Thanks.
I see.

@tompng
Can you update this PR's description?

@tompng
Copy link
Member Author

tompng commented May 2, 2025

@naitoh Updated 👍

@naitoh naitoh merged commit de6f40e into ruby:master May 3, 2025
67 checks passed
@naitoh
Copy link
Contributor

naitoh commented May 3, 2025

Thanks!

@tompng tompng deleted the fix_xpath_reverse_sort branch May 3, 2025 05:16
naitoh added a commit to naitoh/rexml that referenced this pull request May 4, 2025
… for multiple node

## Why?
See: ruby#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>
```
naitoh added a commit to naitoh/rexml that referenced this pull request May 5, 2025
… for multiple node

## Why?
See: ruby#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/x/following-sibling:*[1] => ["w", "x", "y", "z"]
```xml
<div>
  <div>
    <a/> <-- self -->
    <w/> <-- //a/x/following-sibling:*[1] -->
  </div>
  <a/> <-- self -->
  <x/> <-- //a/x/following-sibling:*[1] -->
  <a/> <-- self -->
  <y/> <-- //a/x/following-sibling:*[1] -->
  <a/> <-- self -->
  <z/> <-- //a/x/following-sibling:*[1] -->
</div>
```
naitoh added a commit to naitoh/rexml that referenced this pull request May 5, 2025
… for multiple node

## Why?
See: ruby#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>
```
naitoh added a commit to naitoh/rexml that referenced this pull request May 6, 2025
… for multiple node

## Why?
See: ruby#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>
```
naitoh added a commit to naitoh/rexml that referenced this pull request May 6, 2025
…ing, preceding-sibling

## Why?
See: ruby#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>
```
naitoh added a commit to naitoh/rexml that referenced this pull request May 6, 2025
…ing, preceding-sibling

## Why?
See: ruby#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>
```
naitoh added a commit to naitoh/rexml that referenced this pull request May 6, 2025
…ing, preceding-sibling

## Why?
See: ruby#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>
```
kou pushed a commit that referenced this pull request May 6, 2025
…ing, preceding-sibling (#255)

## Why?
See: #251 (comment)

## Expected values

- 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>
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants