XPath package for Golang, supports HTML, XML, JSON document query.

XPath

GoDoc Coverage Status Build Status Go Report Card

XPath is Go package provides selecting nodes from XML, HTML or other documents using XPath expression.

Implementation

  • htmlquery - an XPath query package for HTML document

  • xmlquery - an XPath query package for XML document.

  • jsonquery - an XPath query package for JSON document

Supported Features

The basic XPath patterns.

The basic XPath patterns cover 90% of the cases that most stylesheets will need.

  • node : Selects all child elements with nodeName of node.

  • * : Selects all child elements.

  • @attr : Selects the attribute attr.

  • @* : Selects all attributes.

  • node() : Matches an org.w3c.dom.Node.

  • text() : Matches a org.w3c.dom.Text node.

  • comment() : Matches a comment.

  • . : Selects the current node.

  • .. : Selects the parent of current node.

  • / : Selects the document node.

  • a[expr] : Select only those nodes matching a which also satisfy the expression expr.

  • a[n] : Selects the nth matching node matching a When a filter's expression is a number, XPath selects based on position.

  • a/b : For each node matching a, add the nodes matching b to the result.

  • a//b : For each node matching a, add the descendant nodes matching b to the result.

  • //b : Returns elements in the entire document matching b.

  • a|b : All nodes matching a or b, union operation(not boolean or).

  • (a, b, c) : Evaluates each of its operands and concatenates the resulting sequences, in order, into a single result sequence

Node Axes

  • child::* : The child axis selects children of the current node.

  • descendant::* : The descendant axis selects descendants of the current node. It is equivalent to '//'.

  • descendant-or-self::* : Selects descendants including the current node.

  • attribute::* : Selects attributes of the current element. It is equivalent to @*

  • following-sibling::* : Selects nodes after the current node.

  • preceding-sibling::* : Selects nodes before the current node.

  • following::* : Selects the first matching node following in document order, excluding descendants.

  • preceding::* : Selects the first matching node preceding in document order, excluding ancestors.

  • parent::* : Selects the parent if it matches. The '..' pattern from the core is equivalent to 'parent::node()'.

  • ancestor::* : Selects matching ancestors.

  • ancestor-or-self::* : Selects ancestors including the current node.

  • self::* : Selects the current node. '.' is equivalent to 'self::node()'.

Expressions

The gxpath supported three types: number, boolean, string.

  • path : Selects nodes based on the path.

  • a = b : Standard comparisons.

    • a = b True if a equals b.
    • a != b True if a is not equal to b.
    • a < b True if a is less than b.
    • a <= b True if a is less than or equal to b.
    • a > b True if a is greater than b.
    • a >= b True if a is greater than or equal to b.
  • a + b : Arithmetic expressions.

    • - a Unary minus
    • a + b Add
    • a - b Substract
    • a * b Multiply
    • a div b Divide
    • a mod b Floating point mod, like Java.
  • a or b : Boolean or operation.

  • a and b : Boolean and operation.

  • (expr) : Parenthesized expressions.

  • fun(arg1, ..., argn) : Function calls:

Function Supported
boolean()
ceiling()
choose()
concat()
contains()
count()
current()
document()
element-available()
ends-with()
false()
floor()
format-number()
function-available()
generate-id()
id()
key()
lang()
last()
local-name()
matches()
name()
namespace-uri()
normalize-space()
not()
number()
position()
replace()
reverse()
round()
starts-with()
string()
string-length()
substring()
substring-after()
substring-before()
sum()
system-property()
translate()
true()
unparsed-entity-url()

Changelogs

2019-03-19

  • optimize XPath | operation performance. #33. Tips: suggest split into multiple subquery if you have a lot of | operations.

2019-01-29

  • improvement normalize-space function. #32

2018-12-07

  • supports XPath 2.0 Sequence expressions. #30 by @minherz.
Owner
The open source web crawler framework project
null
Comments
  • Discussion: Provide `NodeNavigator` interface to get native data

    Discussion: Provide `NodeNavigator` interface to get native data

    When using the xpath library with data-formats that know the data-type of a node (e.g. JSON or Protocol buffer it would be nice to have access to this native underlying value. This has the benefit of being able to parse data with unknown a-priori format and/or data-types.

    The above can be done by adding an interface

    type NodeNavigatorNativeData interface {
    	// NativeValue gets the native value of current node.
    	NativeValue() interface{}
    }
    

    or, alternatively, change the return type of Value() in the NodeNavigator interface. The naming is subject to discussion.

    Please let me know what you think and I can submit a PR for either way.

  • xpath 2.0 - reverse implementation guidance

    xpath 2.0 - reverse implementation guidance

    As I mentioned in https://github.com/antchfx/xpath/issues/44, I need reverse() function which is part of xpath 2.0.

    I did a quick prototyping. I'd like to have main contributors (@zhengchun ?) take a look to see if I'm completely off or not, before I create a pull request:

    Below is the prototype diff (Not many tests added yet). Note the diff is large-ish because I renamed the existing functionQuery to scalarFunctionQuery to differentiate it from my newly added selectFunctionQuery (which currently only has reverse function but I think we can merge unionQuery and logicalQuery into selectFunctionQuery eventually, tell me if I'm crazy)

    diff --git a/build.go b/build.go
    index be1214c..d6ff928 100644
    --- a/build.go
    +++ b/build.go
    @@ -173,7 +173,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     		if err != nil {
     			return nil, err
     		}
    -		qyOutput = &functionQuery{Input: b.firstInput, Func: startwithFunc(arg1, arg2)}
    +		qyOutput = &scalarFunctionQuery{Input: b.firstInput, Func: startwithFunc(arg1, arg2)}
     	case "ends-with":
     		arg1, err := b.processNode(root.Args[0])
     		if err != nil {
    @@ -183,7 +183,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     		if err != nil {
     			return nil, err
     		}
    -		qyOutput = &functionQuery{Input: b.firstInput, Func: endwithFunc(arg1, arg2)}
    +		qyOutput = &scalarFunctionQuery{Input: b.firstInput, Func: endwithFunc(arg1, arg2)}
     	case "contains":
     		arg1, err := b.processNode(root.Args[0])
     		if err != nil {
    @@ -194,7 +194,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     			return nil, err
     		}
     
    -		qyOutput = &functionQuery{Input: b.firstInput, Func: containsFunc(arg1, arg2)}
    +		qyOutput = &scalarFunctionQuery{Input: b.firstInput, Func: containsFunc(arg1, arg2)}
     	case "substring":
     		//substring( string , start [, length] )
     		if len(root.Args) < 2 {
    @@ -215,7 +215,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     				return nil, err
     			}
     		}
    -		qyOutput = &functionQuery{Input: b.firstInput, Func: substringFunc(arg1, arg2, arg3)}
    +		qyOutput = &scalarFunctionQuery{Input: b.firstInput, Func: substringFunc(arg1, arg2, arg3)}
     	case "substring-before", "substring-after":
     		//substring-xxxx( haystack, needle )
     		if len(root.Args) != 2 {
    @@ -231,7 +231,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     		if arg2, err = b.processNode(root.Args[1]); err != nil {
     			return nil, err
     		}
    -		qyOutput = &functionQuery{
    +		qyOutput = &scalarFunctionQuery{
     			Input: b.firstInput,
     			Func:  substringIndFunc(arg1, arg2, root.FuncName == "substring-after"),
     		}
    @@ -244,7 +244,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     		if err != nil {
     			return nil, err
     		}
    -		qyOutput = &functionQuery{Input: b.firstInput, Func: stringLengthFunc(arg1)}
    +		qyOutput = &scalarFunctionQuery{Input: b.firstInput, Func: stringLengthFunc(arg1)}
     	case "normalize-space":
     		if len(root.Args) == 0 {
     			return nil, errors.New("xpath: normalize-space function must have at least one parameter")
    @@ -253,7 +253,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     		if err != nil {
     			return nil, err
     		}
    -		qyOutput = &functionQuery{Input: argQuery, Func: normalizespaceFunc}
    +		qyOutput = &scalarFunctionQuery{Input: argQuery, Func: normalizespaceFunc}
     	case "replace":
     		//replace( string , string, string )
     		if len(root.Args) != 3 {
    @@ -272,7 +272,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     		if arg3, err = b.processNode(root.Args[2]); err != nil {
     			return nil, err
     		}
    -		qyOutput = &functionQuery{Input: b.firstInput, Func: replaceFunc(arg1, arg2, arg3)}
    +		qyOutput = &scalarFunctionQuery{Input: b.firstInput, Func: replaceFunc(arg1, arg2, arg3)}
     	case "translate":
     		//translate( string , string, string )
     		if len(root.Args) != 3 {
    @@ -291,7 +291,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     		if arg3, err = b.processNode(root.Args[2]); err != nil {
     			return nil, err
     		}
    -		qyOutput = &functionQuery{Input: b.firstInput, Func: translateFunc(arg1, arg2, arg3)}
    +		qyOutput = &scalarFunctionQuery{Input: b.firstInput, Func: translateFunc(arg1, arg2, arg3)}
     	case "not":
     		if len(root.Args) == 0 {
     			return nil, errors.New("xpath: not function must have at least one parameter")
    @@ -300,7 +300,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     		if err != nil {
     			return nil, err
     		}
    -		qyOutput = &functionQuery{Input: argQuery, Func: notFunc}
    +		qyOutput = &scalarFunctionQuery{Input: argQuery, Func: notFunc}
     	case "name", "local-name", "namespace-uri":
     		if len(root.Args) > 1 {
     			return nil, fmt.Errorf("xpath: %s function must have at most one parameter", root.FuncName)
    @@ -317,24 +317,24 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     		}
     		switch root.FuncName {
     		case "name":
    -			qyOutput = &functionQuery{Input: b.firstInput, Func: nameFunc(arg)}
    +			qyOutput = &scalarFunctionQuery{Input: b.firstInput, Func: nameFunc(arg)}
     		case "local-name":
    -			qyOutput = &functionQuery{Input: b.firstInput, Func: localNameFunc(arg)}
    +			qyOutput = &scalarFunctionQuery{Input: b.firstInput, Func: localNameFunc(arg)}
     		case "namespace-uri":
    -			qyOutput = &functionQuery{Input: b.firstInput, Func: namespaceFunc(arg)}
    +			qyOutput = &scalarFunctionQuery{Input: b.firstInput, Func: namespaceFunc(arg)}
     		}
     	case "true", "false":
     		val := root.FuncName == "true"
    -		qyOutput = &functionQuery{
    +		qyOutput = &scalarFunctionQuery{
     			Input: b.firstInput,
     			Func: func(_ query, _ iterator) interface{} {
     				return val
     			},
     		}
     	case "last":
    -		qyOutput = &functionQuery{Input: b.firstInput, Func: lastFunc}
    +		qyOutput = &scalarFunctionQuery{Input: b.firstInput, Func: lastFunc}
     	case "position":
    -		qyOutput = &functionQuery{Input: b.firstInput, Func: positionFunc}
    +		qyOutput = &scalarFunctionQuery{Input: b.firstInput, Func: positionFunc}
     	case "boolean", "number", "string":
     		inp := b.firstInput
     		if len(root.Args) > 1 {
    @@ -347,7 +347,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     			}
     			inp = argQuery
     		}
    -		f := &functionQuery{Input: inp}
    +		f := &scalarFunctionQuery{Input: inp}
     		switch root.FuncName {
     		case "boolean":
     			f.Func = booleanFunc
    @@ -368,7 +368,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     		if err != nil {
     			return nil, err
     		}
    -		qyOutput = &functionQuery{Input: argQuery, Func: countFunc}
    +		qyOutput = &scalarFunctionQuery{Input: argQuery, Func: countFunc}
     	case "sum":
     		if len(root.Args) == 0 {
     			return nil, fmt.Errorf("xpath: sum(node-sets) function must with have parameters node-sets")
    @@ -377,7 +377,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     		if err != nil {
     			return nil, err
     		}
    -		qyOutput = &functionQuery{Input: argQuery, Func: sumFunc}
    +		qyOutput = &scalarFunctionQuery{Input: argQuery, Func: sumFunc}
     	case "ceiling", "floor", "round":
     		if len(root.Args) == 0 {
     			return nil, fmt.Errorf("xpath: ceiling(node-sets) function must with have parameters node-sets")
    @@ -386,7 +386,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     		if err != nil {
     			return nil, err
     		}
    -		f := &functionQuery{Input: argQuery}
    +		f := &scalarFunctionQuery{Input: argQuery}
     		switch root.FuncName {
     		case "ceiling":
     			f.Func = ceilingFunc
    @@ -408,7 +408,16 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
     			}
     			args = append(args, q)
     		}
    -		qyOutput = &functionQuery{Input: b.firstInput, Func: concatFunc(args...)}
    +		qyOutput = &scalarFunctionQuery{Input: b.firstInput, Func: concatFunc(args...)}
    +    case "reverse":
    +        if len(root.Args) == 0 {
    +            return nil, fmt.Errorf("xpath: reverse(node-sets) function must with have parameters node-sets")
    +        }
    +        argQuery, err := b.processNode(root.Args[0])
    +        if err != nil {
    +            return nil, err
    +        }
    +        qyOutput = &selectFunctionQuery{Input: argQuery, Func: reverseFunc}
     	default:
     		return nil, fmt.Errorf("not yet support this function %s()", root.FuncName)
     	}
    @@ -464,7 +473,7 @@ func (b *builder) processOperatorNode(root *operatorNode) (query, error) {
     		qyOutput = &booleanQuery{Left: left, Right: right, IsOr: isOr}
     	case "|":
     		qyOutput = &unionQuery{Left: left, Right: right}
    -	}
    +    }
     	return qyOutput, nil
     }
     
    diff --git a/func.go b/func.go
    index 1edeb1d..12d0ec2 100644
    --- a/func.go
    +++ b/func.go
    @@ -526,8 +526,28 @@ func concatFunc(args ...query) func(query, iterator) interface{} {
     
     // https://github.com/antchfx/xpath/issues/43
     func functionArgs(q query) query {
    -	if _, ok := q.(*functionQuery); ok {
    +	if _, ok := q.(*scalarFunctionQuery); ok {
     		return q
     	}
     	return q.Clone()
     }
    +
    +func reverseFunc(q query, t iterator) func() NodeNavigator {
    +    var list []NodeNavigator
    +    for {
    +        node := q.Select(t)
    +        if node == nil {
    +            break
    +        }
    +        list = append(list, node.Copy())
    +    }
    +    i := len(list) - 1
    +    return func() NodeNavigator {
    +        if i < 0 {
    +            return nil
    +        }
    +        node := list[i]
    +        i--
    +        return node
    +    }
    +}
    diff --git a/query.go b/query.go
    index afeb890..115fdc8 100644
    --- a/query.go
    +++ b/query.go
    @@ -590,25 +590,48 @@ func (f *filterQuery) Clone() query {
     	return &filterQuery{Input: f.Input.Clone(), Predicate: f.Predicate.Clone()}
     }
     
    -// functionQuery is an XPath function that call a function to returns
    +// scalarFunctionQuery is an XPath function that call a function to returns
     // value of current NodeNavigator node.
    -type functionQuery struct {
    +type scalarFunctionQuery struct {
     	Input query                             // Node Set
     	Func  func(query, iterator) interface{} // The xpath function.
     }
     
    -func (f *functionQuery) Select(t iterator) NodeNavigator {
    +func (f *scalarFunctionQuery) Select(t iterator) NodeNavigator {
     	return nil
     }
     
     // Evaluate call a specified function that will returns the
     // following value type: number,string,boolean.
    -func (f *functionQuery) Evaluate(t iterator) interface{} {
    +func (f *scalarFunctionQuery) Evaluate(t iterator) interface{} {
     	return f.Func(f.Input, t)
     }
     
    -func (f *functionQuery) Clone() query {
    -	return &functionQuery{Input: f.Input.Clone(), Func: f.Func}
    +func (f *scalarFunctionQuery) Clone() query {
    +	return &scalarFunctionQuery{Input: f.Input.Clone(), Func: f.Func}
    +}
    +
    +type selectFunctionQuery struct {
    +    Input    query
    +    Func     func(query, iterator) func() NodeNavigator
    +    iterator func() NodeNavigator
    +}
    +
    +func (f *selectFunctionQuery) Select(t iterator) NodeNavigator {
    +    if f.iterator == nil {
    +        f.iterator = f.Func(f.Input, t)
    +    }
    +    return f.iterator()
    +}
    +
    +func (f *selectFunctionQuery) Evaluate(t iterator) interface{} {
    +    // QUESTION: when is this ever called?
    +    f.iterator = f.Func(f.Input, t)
    +    return f
    +}
    +
    +func (f *selectFunctionQuery) Clone() query {
    +    return &selectFunctionQuery{Input: f.Input.Clone(), Func: f.Func}
     }
     
     // constantQuery is an XPath constant operand.
    diff --git a/xpath_test.go b/xpath_test.go
    index 7b7d14b..edb222d 100644
    --- a/xpath_test.go
    +++ b/xpath_test.go
    @@ -1,9 +1,9 @@
     package xpath
     
     import (
    -	"bytes"
    -	"strings"
    -	"testing"
    +    "bytes"
    +    "strings"
    +    "testing"
     )
     
     var (
    @@ -729,3 +729,12 @@ func example() *TNode {
     
     	return xhtml
     }
    +
    +func TestReverse(t *testing.T) {
    +    list := selectNodes(html, "reverse(//li)")
    +    if len(list) != 4 { t.Fatal("reverse(//li) should return 4 <li> nodes") }
    +    if list[0].Value() != "" { t.Fatal("reverse(//li)[0].Value() should be ''") }
    +    if list[1].Value() != "login" { t.Fatal("reverse(//li)[0].Value() should be 'login'") }
    +    if list[2].Value() != "about" { t.Fatal("reverse(//li)[0].Value() should be 'about'") }
    +    if list[3].Value() != "Home" { t.Fatal("reverse(//li)[0].Value() should be 'Home'") }
    +}
    

    Thank you very much!

  • Function substring returns boolean

    Function substring returns boolean

    If we use the test condition below it will panic. It panics because in this case substring returns false (boolen), so when it try to compare false = '' it fails. If we remove the compare condition it works fine, but should substring work as boolen condition?

    Panic: testXPath2(t, html, "//title[substring(child::*,0) = '']", 0)

    Work: testXPath2(t, html, "//title[substring(child::*,0)]", 0)

  • Implement `matches` XPath function.

    Implement `matches` XPath function.

    See details in https://github.com/antchfx/xpath/issues/57.

    Also created a number of assert???? helpers in assert_test.go and moved assertPanic into it.

  • xpath /foo/bar@attr doesn't match the attribute node but the bar element

    xpath /foo/bar@attr doesn't match the attribute node but the bar element

    In the following document:

    <?xml version="1.0"?>
    <root>
    	<foo>
    		<bar attr="b"/>
    	</foo>
    </root>
    

    the xpath expression /foo/bar@attr matches <bar/> instead of attr="b"

    Test case in https://github.com/antchfx/xpath/pull/8 instantiated in https://github.com/antchfx/xquery/pull/19

  • Turning xpath into dom manipulation tool as well

    Turning xpath into dom manipulation tool as well

    I know it sounds odd at first but please hear me out.

    I've been searching for an XML-2.0-compatible Go based XML manipulation tool/lib for a long long time, and I believe there is no such tool/lib exist. I can go on and on about the tools that I looked into before, but please do let me know if I'm wrong, that there is indeed such Go based XML manipulation tool/lib out there, which can satisfy the following simple requirements,

    • locate XML node with XPath 1.0/2.0 query
    • do basic xml node manipulation like, add/delete/change attribute, create/insert/append/delete ChildNode, etc

    I saw that this xpath package already has functions like addAttribute, createChildNode, so the first step making xpath into a dom manipulation tool is simple, just expose those ready-made functions as public, and adding the missing functionalities would be step 2.

    What would you say?

  • preceding-sibling::[n] returns incorrect value for n > 1

    preceding-sibling::[n] returns incorrect value for n > 1

    Using xmlquery, the following program outputs preceding-sibling::*[2] not found, even though both the XPath expressions are correct, and should evaluate to node b and c respectively:

    package main
    
    import "strings"
    import "github.com/antchfx/xmlquery"
    
    func main() {
    	doc, _ := xmlquery.Parse(strings.NewReader(`<root>
    <a>
    <b id="b"></b>
    <c id="c"></c>
    <d id="d"></d>
    </a>
    </root>`))
    
    	if node := xmlquery.FindOne(doc, `//*[@id="d"]/preceding-sibling::*[2]`); node == nil {
    		println("preceding-sibling::*[2] not found")
    	}
    
    	if node := xmlquery.FindOne(doc, `//*[@id="d"]/preceding-sibling::*[1]`); node == nil {
    		println("preceding-sibling::*[1] not found")
    	}
    }
    

    Maybe related to https://github.com/antchfx/xpath/issues/36?

  • child::node() is not selecting text nodes

    child::node() is not selecting text nodes

    Given the following document:

    <root>
    <para> text <img/> text </para>
    </root>
    

    Given the context node selected by //para

    When selecting child nodes using ./child::node()

    Then, only the <img/> node is selected instead of all three nodes including the two text nodes. I believe this is a similar issue to #11: the default predicate for all queries is to select only element nodes.

  • fix parsing bug #7 (in xmlquery)

    fix parsing bug #7 (in xmlquery)

    This PR fixes #7 (from xmlquery repository).

    While working on it I discovered another bug that fails to parse a filter if it is after self or parent step element. I fixed it without opening another issue because it is part of the xpath example in #7.

  • xpath with count does not return anything

    xpath with count does not return anything

    Usecase: http://xpather.com/Kp2lY28A

    I am using a xpath to select all p elements containing sup elements between tables.

    for i := 1; i < 10; i++ {
    	xp := fmt.Sprintf("//table[%v]/following-sibling::p[child::sup][count(.|//table[%v]/preceding-sibling::p[child::sup])=count(//table[%v]/preceding-sibling::p[child::sup])]", i, i+1, i+1)
    	expr, err := xpath.Compile(xp)
    	if err != nil {
    		logger.Log.Error(err)
    		return nil, "", err
    	}
    	iter := expr.Evaluate(htmlquery.CreateXPathNavigator(dc)).(*xpath.NodeIterator)
    	for iter.MoveNext() {
    		logger.Log.Debug(iter.Current().Value())
    	}
    }
    

    The final xpath is //table[1]/following-sibling::p[child::sup][count(.|//table[2]/preceding-sibling::p[child::sup])=count(//table[2]/preceding-sibling::p[child::sup])]. When I test this xpath in the usecase it returns the correct values. Am I using a xpath which isn't supported or is it a bug somewhere in this library?

  • //root/p[@attr='val'] is not working

    //root/p[@attr='val'] is not working

    I have the following example document:

    <?xml version="1.0"?>
    <root>
    	<foo>
    		<bar a="b"/>
    	</foo>
    	<li>
    		<a class="tag" href=""/>
    	</li>
    	<p class='meta'>
    	</p>
    </root>
    

    with the following xpath expression: //root/p[@class='meta']

    The expression does not find the <p/> node while the XPath tester can.

  • string comparison size error

    string comparison size error

    use xmlquery find func, use string comparison size exist error, i think cmpStringStringF have bug。

    example: test data: ab find expr: /res/value[data > "a"] expected: b output: a

    `// operator.go func cmpNodeSetString(t iterator, op string, m, n interface{}) bool {

    a := m.(query)
    b := n.(string)
    for {
    	node := a.Select(t)
    	if node == nil {
    		break
    	}
               // this is errror
    	if cmpStringStringF(op, b, node.Value()) {
    		return true
    	}
                // this is should 
    	//if cmpStringStringF(op, node.Value(),b) {
    	//	return true
    	//}
    }
    return false
    

    }`

  • The multiple filter query with `last()` function

    The multiple filter query with `last()` function

    <root>
    	<test foo="bar">First</test>
    	<test foo="bar">Second</test>
    	<test foo="xyz">Third</test>
    </root>
    

    Test:

    • [x] [//test[@foo='bar'])/[last()]`

    This from https://github.com/antchfx/xpath/issues/76

    expected:

    <test foo="bar">Second</test>
    

    actually:

    not found

    • [ ] //test[@foo='bar'][last()]

    expected

    <test foo="bar">Second</test>
    

    actually:

    not found

  • last() not working on subgroups

    last() not working on subgroups

    Hello,

    first of all, thanks for your amazing work, it's really useful to me.

    I'm having an issue when using the last() function on a subgroup, for example on a result of another filter.

    For example, this piece of code:

    package main
    
    import (
    	"fmt"
    	"github.com/antchfx/xmlquery"
    	"strings"
    )
    
    func main() {
    	xml := `
    	<root>
    		<test foo="bar">First</test>
    		<test foo="bar">Second</test>
    		<test foo="xyz">Third</test>
    	</root>
    	`
    
    	doc, err := xmlquery.Parse(strings.NewReader(xml))
    	if err != nil {
    		panic(err)
    	}
    
    	tests, err := xmlquery.QueryAll(doc, `(//test[@foo="bar"])[last()]`)
    	if err != nil {
    		panic(err)
    	}
    	for _, test := range tests {
    		fmt.Println(test.InnerText())
    	}
    }
    

    I would expect this to print "Second", but it prints nothing, because the output from the query has no elements.

    Am I doing something wrong, or is this an issue on the last() function?

    Thank you!

  • Implementing `current()`

    Implementing `current()`

    I'm wondering why the current() function hasn't been implemented, and if it would make much time to do so.

    I'm currently in the situation where I would need a query like //cd[@title=current()/@ref]. The node navigator used for the expression evaluate isn't the document root, but a previously selected node.

  • Cyrillic characters support

    Cyrillic characters support

    Changed type of field text of struct scanner into slice of rune to support cyrillic symbols. Since the Cyrillic character is encoded in two bytes, the length of the string does not equal the number of characters. Therefore, an error occurred when using Cyrillic characters.

Go encoding/xml package that improves support for XML namespaces

encoding/xml with namespaces This is a fork of the Go encoding/xml package that improves support for XML namespaces, kept in sync with golang/go#48641

Nov 11, 2022
Gosaxml is a streaming XML decoder and encoder, similar in interface to the encoding/xml

gosaxml is a streaming XML decoder and encoder, similar in interface to the encoding/xml, but with a focus on performance, low memory footprint and on

Aug 21, 2022
XML to MAP converter written Golang

xml2map XML to MAP converter written Golang Sometimes there is a need for the representation of previously unknown structures. Such a universal repres

Dec 8, 2022
xmlwriter is a pure-Go library providing procedural XML generation based on libxml2's xmlwriter module

xmlwriter xmlwriter is a pure-Go library providing a procedural XML generation API based on libxml2's xmlwriter module. The package is extensively doc

Sep 27, 2022
Generate a Go struct from XML.
Generate a Go struct from XML.

zek Zek is a prototype for creating a Go struct from an XML document. The resulting struct works best for reading XML (see also #14), to create XML, y

Dec 30, 2022
xmlquery is Golang XPath package for XML query.

xmlquery Overview xmlquery is an XPath query package for XML documents, allowing you to extract data or evaluate from XML documents with an XPath expr

Jan 1, 2023
htmlquery is golang XPath package for HTML query.

htmlquery Overview htmlquery is an XPath query package for HTML, lets you extract data or evaluate from HTML documents by an XPath expression. htmlque

Jan 4, 2023
Extract data or evaluate value from HTML/XML documents using XPath

xquery NOTE: This package is deprecated. Recommends use htmlquery and xmlquery package, get latest version to fixed some issues. Overview Golang packa

Nov 9, 2022
Query, update and convert data structures from the command line. Comparable to jq/yq but supports JSON, TOML, YAML, XML and CSV with zero runtime dependencies.
Query, update and convert data structures from the command line. Comparable to jq/yq but supports JSON, TOML, YAML, XML and CSV with zero runtime dependencies.

dasel Dasel (short for data-selector) allows you to query and modify data structures using selector strings. Comparable to jq / yq, but supports JSON,

Jan 2, 2023
Query, update and convert data structures from the command line. Comparable to jq/yq but supports JSON, TOML, YAML, XML and CSV with zero runtime dependencies.
Query, update and convert data structures from the command line. Comparable to jq/yq but supports JSON, TOML, YAML, XML and CSV with zero runtime dependencies.

dasel Dasel (short for data-selector) allows you to query and modify data structures using selector strings. Comparable to jq / yq, but supports JSON,

Jan 2, 2023
Simple system for writing HTML/XML as Go code. Better-performing replacement for html/template and text/template

Simple system for writing HTML as Go code. Use normal Go conditionals, loops and functions. Benefit from typing and code analysis. Better performance than templating. Tiny and dependency-free.

Dec 5, 2022
Produces a set of tags from given source. Source can be either an HTML page, Markdown document or a plain text. Supports English, Russian, Chinese, Hindi, Spanish, Arabic, Japanese, German, Hebrew, French and Korean languages.
Produces a set of tags from given source. Source can be either an HTML page, Markdown document or a plain text. Supports English, Russian, Chinese, Hindi, Spanish, Arabic, Japanese, German, Hebrew, French and Korean languages.

Tagify Gets STDIN, file or HTTP address as an input and returns a list of most popular words ordered by popularity as an output. More info about what

Dec 19, 2022
Go package that handles HTML, JSON, XML and etc. responses

gores http response utility library for Go this package is very small and lightweight, useful for RESTful APIs. installation go get github.com/alioygu

Oct 31, 2022
Go package for easily rendering JSON, XML, binary data, and HTML templates responses.

Render Render is a package that provides functionality for easily rendering JSON, XML, text, binary data, and HTML templates. This package is based on

Jan 8, 2023
Simple, lightweight and faster response (JSON, JSONP, XML, YAML, HTML, File) rendering package for Go

Package renderer Simple, lightweight and faster response (JSON, JSONP, XML, YAML, HTML, File) rendering package for Go Installation Install the packag

Dec 13, 2022
A simple Go package to Query over JSON/YAML/XML/CSV Data
A simple Go package to Query over JSON/YAML/XML/CSV Data

A simple Go package to Query over JSON Data. It provides simple, elegant and fast ODM like API to access, query JSON document Installation Install the

Jan 8, 2023
A simple Go package to Query over JSON/YAML/XML/CSV Data
A simple Go package to Query over JSON/YAML/XML/CSV Data

A simple Go package to Query over JSON Data. It provides simple, elegant and fast ODM like API to access, query JSON document Installation Install the

Dec 27, 2022
Powerful and versatile MIME sniffing package using pre-compiled glob patterns, magic number signatures, XML document namespaces, and tree magic for mounted volumes, generated from the XDG shared-mime-info database.

mimemagic Powerful and versatile MIME sniffing package using pre-compiled glob patterns, magic number signatures, xml document namespaces, and tree ma

Nov 3, 2022
Go encoding/xml package that improves support for XML namespaces

encoding/xml with namespaces This is a fork of the Go encoding/xml package that improves support for XML namespaces, kept in sync with golang/go#48641

Nov 11, 2022
PretGO - asic cli for format json,html and xml!
 PretGO - asic cli for format json,html and xml!

PretGO So basic cli for format json,html and xml! Table of contents Screenshots Setup Status Contact Screenshots Setup First clone project git clone h

Sep 2, 2022