Merge pull request #1453 from opencloud-eu/dependabot/go_modules/github.com/beevik/etree-1.6.0

build(deps): bump github.com/beevik/etree from 1.5.1 to 1.6.0
This commit is contained in:
Ralf Haferkamp
2025-09-04 08:41:01 +02:00
committed by GitHub
7 changed files with 105 additions and 56 deletions

2
go.mod
View File

@@ -10,7 +10,7 @@ require (
github.com/MicahParks/keyfunc/v2 v2.1.0
github.com/Nerzal/gocloak/v13 v13.9.0
github.com/bbalet/stopwords v1.0.0
github.com/beevik/etree v1.5.1
github.com/beevik/etree v1.6.0
github.com/blevesearch/bleve/v2 v2.5.3
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/coreos/go-oidc/v3 v3.15.0

4
go.sum
View File

@@ -136,8 +136,8 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W
github.com/aws/aws-sdk-go v1.37.27/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/bbalet/stopwords v1.0.0 h1:0TnGycCtY0zZi4ltKoOGRFIlZHv0WqpoIGUsObjztfo=
github.com/bbalet/stopwords v1.0.0/go.mod h1:sAWrQoDMfqARGIn4s6dp7OW7ISrshUD8IP2q3KoqPjc=
github.com/beevik/etree v1.5.1 h1:TC3zyxYp+81wAmbsi8SWUpZCurbxa6S8RITYRSkNRwo=
github.com/beevik/etree v1.5.1/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs=
github.com/beevik/etree v1.6.0 h1:u8Kwy8pp9D9XeITj2Z0XtA5qqZEmtJtuXZRQi+j03eE=
github.com/beevik/etree v1.6.0/go.mod h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=

View File

@@ -20,6 +20,8 @@ Some of the package's capabilities and features:
* Built on top of the go [encoding/xml](http://golang.org/pkg/encoding/xml)
package.
The etree package is compatible with go versions 1.23 and later.
### Creating an XML document
The following example creates an XML document from scratch using the etree
@@ -114,7 +116,7 @@ etree selection queries.
root := doc.SelectElement("bookstore")
fmt.Println("ROOT element:", root.Tag)
for _, book := range root.SelectElements("book") {
for _, book := range root.SelectElementsSeq("book") {
fmt.Println("CHILD element:", book.Tag)
if title := book.SelectElement("title"); title != nil {
lang := title.SelectAttrValue("lang", "unknown")
@@ -149,7 +151,7 @@ into the category of 'WEB'. The double-slash prefix in the path causes the
search for book elements to occur recursively; book elements may appear at any
level of the XML hierarchy.
```go
for _, t := range doc.FindElements("//book[@category='WEB']/title") {
for _, t := range doc.FindElementsSeq("//book[@category='WEB']/title") {
fmt.Println("Title:", t.Text())
}
```
@@ -163,7 +165,7 @@ Title: Learning XML
This example finds the first book element under the root bookstore element and
outputs the tag and text of each of its child elements.
```go
for _, e := range doc.FindElements("./bookstore/book[1]/*") {
for _, e := range doc.FindElementsSeq("./bookstore/book[1]/*") {
fmt.Printf("%s: %s\n", e.Tag, e.Text())
}
```
@@ -179,7 +181,7 @@ price: 30.00
This example finds all books with a price of 49.99 and outputs their titles.
```go
path := etree.MustCompilePath("./bookstore/book[p:price='49.99']/title")
for _, e := range doc.FindElementsPath(path) {
for _, e := range doc.FindElementsPathSeq(path) {
fmt.Println(e.Text())
}
```
@@ -189,8 +191,8 @@ Output:
XQuery Kick Start
```
Note that this example uses the FindElementsPath function, which takes as an
argument a pre-compiled path object. Use precompiled paths when you plan to
Note that this example uses the `FindElementsPathSeq` function, which takes as
an argument a pre-compiled path object. Use precompiled paths when you plan to
search with the same path more than once.
### Other features

View File

@@ -1,3 +1,14 @@
Release 1.6.0
=============
**Changes**
* Added new iterator versions of existing functions that return slices of
`Element` pointers: `ChildElementsSeq`, `SelectElementsSeq`,
`FindElementsSeq`, and `FindElementsPathSeq`.
* Improved performance of functions that return a single element.
* Because of its use of iterators, this package now requires go 1.23 or later.
Release 1.5.1
=============

View File

@@ -12,6 +12,7 @@ import (
"encoding/xml"
"errors"
"io"
"iter"
"os"
"slices"
"strings"
@@ -1018,24 +1019,29 @@ func (e *Element) SelectAttrValue(key, dflt string) string {
// ChildElements returns all elements that are children of this element.
func (e *Element) ChildElements() []*Element {
var elements []*Element
for _, t := range e.Child {
if c, ok := t.(*Element); ok {
elements = append(elements, c)
return slices.Collect(e.ChildElementsSeq())
}
// ChildElementsSeq returns an iterator over all child elements of this
// element.
func (e *Element) ChildElementsSeq() iter.Seq[*Element] {
return func(yield func(*Element) bool) {
for _, t := range e.Child {
if c, ok := t.(*Element); ok {
if !yield(c) {
return
}
}
}
}
return elements
}
// SelectElement returns the first child element with the given 'tag' (i.e.,
// name). The function returns nil if no child element matching the tag is
// found. The tag may include a namespace prefix followed by a colon.
func (e *Element) SelectElement(tag string) *Element {
space, stag := spaceDecompose(tag)
for _, t := range e.Child {
if c, ok := t.(*Element); ok && spaceMatch(space, c.Space) && stag == c.Tag {
return c
}
for element := range e.SelectElementsSeq(tag) {
return element
}
return nil
}
@@ -1043,14 +1049,23 @@ func (e *Element) SelectElement(tag string) *Element {
// SelectElements returns a slice of all child elements with the given 'tag'
// (i.e., name). The tag may include a namespace prefix followed by a colon.
func (e *Element) SelectElements(tag string) []*Element {
space, stag := spaceDecompose(tag)
var elements []*Element
for _, t := range e.Child {
if c, ok := t.(*Element); ok && spaceMatch(space, c.Space) && stag == c.Tag {
elements = append(elements, c)
return slices.Collect(e.SelectElementsSeq(tag))
}
// SelectElementsSeq returns an iterator over all child elements with the
// given 'tag' (i.e., name). The tag may include a namespace prefix followed
// by a colon.
func (e *Element) SelectElementsSeq(tag string) iter.Seq[*Element] {
return func(yield func(*Element) bool) {
space, stag := spaceDecompose(tag)
for _, t := range e.Child {
if c, ok := t.(*Element); ok && spaceMatch(space, c.Space) && stag == c.Tag {
if !yield(c) {
return
}
}
}
}
return elements
}
// FindElement returns the first element matched by the XPath-like 'path'
@@ -1063,10 +1078,8 @@ func (e *Element) FindElement(path string) *Element {
// FindElementPath returns the first element matched by the 'path' object. The
// function returns nil if no element is found using the path.
func (e *Element) FindElementPath(path Path) *Element {
p := newPather()
elements := p.traverse(e, path)
if len(elements) > 0 {
return elements[0]
for element := range path.traverse(e) {
return element
}
return nil
}
@@ -1075,13 +1088,26 @@ func (e *Element) FindElementPath(path Path) *Element {
// string. The function returns nil if no child element is found using the
// path. It panics if an invalid path string is supplied.
func (e *Element) FindElements(path string) []*Element {
return e.FindElementsPath(MustCompilePath(path))
return slices.Collect(e.FindElementsSeq(path))
}
// FindElementsSeq returns an iterator over elements matched by the XPath-like
// 'path' string. This function uses Go's iterator support for
// memory-efficient traversal. It panics if an invalid path string is
// supplied.
func (e *Element) FindElementsSeq(path string) iter.Seq[*Element] {
return e.FindElementsPathSeq(MustCompilePath(path))
}
// FindElementsPath returns a slice of elements matched by the 'path' object.
func (e *Element) FindElementsPath(path Path) []*Element {
p := newPather()
return p.traverse(e, path)
return slices.Collect(e.FindElementsPathSeq(path))
}
// FindElementsPathSeq returns an iterator over elements matched by the 'path'
// object.
func (e *Element) FindElementsPathSeq(path Path) iter.Seq[*Element] {
return path.traverse(e)
}
// NotNil returns the receiver element if it isn't nil; otherwise, it returns

View File

@@ -5,6 +5,7 @@
package etree
import (
"iter"
"strconv"
"strings"
)
@@ -122,6 +123,20 @@ func MustCompilePath(path string) Path {
return p
}
// traverse follows the path from the element e, yielding elements that match
// the path's selectors and filters using iterators.
func (p Path) traverse(e *Element) iter.Seq[*Element] {
pather := newPather()
return func(yield func(*Element) bool) {
pather.queue.add(node{e, p.segments})
for pather.queue.len() > 0 {
if cont := pather.eval(pather.queue.remove(), yield); !cont {
return
}
}
}
}
// A segment is a portion of a path between "/" characters.
// It contains one selector and zero or more [filters].
type segment struct {
@@ -148,6 +163,13 @@ type filter interface {
apply(p *pather)
}
// A node represents an element and the remaining path segments that
// should be applied against it by the pather.
type node struct {
e *Element
segments []segment
}
// A pather is helper object that traverses an element tree using
// a Path object. It collects and deduplicates all elements matching
// the path query.
@@ -159,13 +181,7 @@ type pather struct {
scratch []*Element // used by filters
}
// A node represents an element and the remaining path segments that
// should be applied against it by the pather.
type node struct {
e *Element
segments []segment
}
// newPather creates a new pather instance.
func newPather() *pather {
return &pather{
results: make([]*Element, 0),
@@ -175,20 +191,11 @@ func newPather() *pather {
}
}
// traverse follows the path from the element e, collecting
// and then returning all elements that match the path's selectors
// and filters.
func (p *pather) traverse(e *Element, path Path) []*Element {
for p.queue.add(node{e, path.segments}); p.queue.len() > 0; {
p.eval(p.queue.remove())
}
return p.results
}
// eval evaluates the current path node by applying the remaining
// path's selector rules against the node's element.
func (p *pather) eval(n node) {
p.candidates = p.candidates[0:0]
// eval evaluates the current path node by applying the remaining path's
// selector rules against the node's element, yielding results via iterator.
// Returns false if early termination is requested.
func (p *pather) eval(n node, yield func(*Element) bool) bool {
p.candidates = p.candidates[:0]
seg, remain := n.segments[0], n.segments[1:]
seg.apply(n.e, p)
@@ -196,7 +203,9 @@ func (p *pather) eval(n node) {
for _, c := range p.candidates {
if in := p.inResults[c]; !in {
p.inResults[c] = true
p.results = append(p.results, c)
if !yield(c) {
return false
}
}
}
} else {
@@ -204,6 +213,7 @@ func (p *pather) eval(n node) {
p.queue.add(node{c, remain})
}
}
return true
}
// A compiler generates a compiled path from a path string.

4
vendor/modules.txt vendored
View File

@@ -101,8 +101,8 @@ github.com/asaskevich/govalidator
# github.com/bbalet/stopwords v1.0.0
## explicit
github.com/bbalet/stopwords
# github.com/beevik/etree v1.5.1
## explicit; go 1.21.0
# github.com/beevik/etree v1.6.0
## explicit; go 1.23.0
github.com/beevik/etree
# github.com/beorn7/perks v1.0.1
## explicit; go 1.11