Library / SDK
antchfx/xpath avatar
antchfx/xpath

antchfx/xpath, an XPath engine for Go with HTML, XML and JSON back ends

XPath package for golang, supports HTML, XML, JSON document query and more

742 stars112 forksGoMIT

At a glance

What is it?
antchfx/xpath is a Go package that evaluates XPath expressions against a document tree, and the author has built three query packages on top of it for HTML, XML and JSON respectively. The implementation covers the basic patterns, the node axes and the expression grammar, and it is MIT licensed with frequent releases.
Who is it for?
Adopt antchfx/xpath if you need XPath evaluation in Go, or one of the three query packages built on it for scraping HTML, reading XML or navigating JSON. Skip it if your documents are small and a struct decode or a JSON path library would do the job more simply.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The engine and the three packages on top of it

XPath is a Go package that provides selecting nodes from XML, HTML or other documents using an XPath expression, and it is the engine rather than the query layer.

The distinction is worth being clear about, because the repository lists three separate implementations and it is easy to pick the wrong one. htmlquery is an XPath query package for HTML documents. xmlquery is an XPath query package for XML documents. jsonquery is an XPath query package for JSON documents.

Each is a separate repository with its own parser, and the XPath engine is shared underneath. That arrangement matters for two reasons. The parsers differ, because HTML needs a lenient, error-recovering parser and XML does not, and because the node model exposed to the XPath expression differs: an HTML document has no namespaces in practice and a forgiving tree, while an XML tree has namespace-aware nodes and a strict structure. JSON has neither, so jsonquery has to synthesise a node model that XPath can walk, which is an interesting problem in its own right.

So the choice of package is a choice of document type, and the choice of this repository is whether you want the expression evaluation without the query conveniences. For most uses you want one of the three siblings, and this one is the piece they share.

The package is MIT licensed, on the master branch, with the last push on 2026-10-16 and releases v1.3.7, v1.3.8 and v1.3.9 between July and October 2026. That cadence, a patch release every few weeks, is what you would expect from a library that tracks a specification and a language ecosystem.

The README links GoDoc, a coverage badge, a build status workflow, a Go Report Card badge and a coveralls coverage status, so the project publishes its test coverage rather than leaving it implicit.

What the expression language covers

The supported features are listed in detail, and the README's framing of the basic set is useful: the basic XPath patterns cover 90 percent of the cases that most stylesheets will need.

The basic patterns are the ones you would expect. node selects all child elements with a given nodeName, and * selects all child elements. @attr and @* select attributes. The node() function matches an org.w3c.dom.Node, text() matches a Text node and comment() matches a comment. A dot selects the current node and two dots the parent, and a slash selects the document node.

The path expressions are the familiar ones. a[expr] selects only those nodes matching a which also satisfy the expression, and a[n] selects the nth matching node, with the note that when a filter's expression is a number XPath selects based on position. a/b adds the nodes matching b to the result for each node matching a, a//b does the same for descendants, and //b returns elements in the entire document matching b. The union a|b returns all nodes matching a or b, and is explicitly noted as a union operation rather than a boolean or. Parenthesised comma lists concatenate the resulting sequences in order into a single result sequence, and (a/b) selects all matching nodes as a grouping set.

The node axes are covered comprehensively. child, descendant, descendant-or-self, attribute, following-sibling, preceding-sibling, following, preceding, parent, ancestor, ancestor-or-self and self are all listed with their shorthand equivalents, so the usual abbreviations resolve to the same thing.

The expression grammar supports the three value types, number, boolean and string. Comparisons with =, !=, <, <=, > and >= are standard. Arithmetic includes addition, subtraction, multiplication, division with the div keyword, modulus with mod, and unary minus. The boolean operators or and and are there, along with parentheses and function calls in the form fun(arg1, ..., argn).

The function table is the place where the gaps show, and it is worth reading before you commit. boolean, ceiling and choose are listed, and the table marks each function as supported or not, so the boundary is explicit rather than something you discover when a query returns nothing.

How the pieces fit, and where the sharp edges are

The design is conventional and the layering is the interesting part. The engine walks a node tree that satisfies an interface, and the three query packages each supply their own document representation. The XPath semantics are then identical across HTML, XML and JSON, which is the point of the separation.

That has a nice consequence for a codebase that handles more than one format. A crawler that reads XML feeds and then HTML pages can use the same expression syntax for both, because the expression is evaluated against a node model rather than against a specific parser's output.

The sharp edges are the ones you would expect from a partial implementation of a large specification. The function table with its ticks and crosses is the first place to look, and the practical advice is to keep expressions within the basic patterns unless you have checked the specific function you need. XPath 2.0 functions in particular are a large surface, and the topics list xpath2 alongside xpath and xpath-patterns, which suggests some of the second-generation work is present and some is not.

The second edge is HTML. XPath was designed for XML, where the document is well formed and the tree reflects the data. HTML is not well formed, the parser has to recover from unclosed tags, and the resulting tree is a best effort. An expression that works on well-formed XML can return surprising results on HTML, because the recovered tree has nodes in places the source did not imply.

The third is JSON. Mapping a JSON document onto an XPath node model is not standard, because JSON has objects, arrays and values rather than elements, attributes and text. The sibling jsonquery package has to make choices about how an object member becomes an element and how an array becomes a set of children, and those choices determine what expressions are meaningful. The htmlquery, xmlquery and jsonquery repositories are where that documentation lives.

One more practical point. XPath evaluation over a large HTML document is not free, and the cost depends on how the expression narrows. A path like //div//a walks a large part of the tree, while a path rooted at a specific element with a predicate does not. That is true of every XPath implementation, but it matters more in Go where people expect the speed of a compiled parser.

How it fits against the alternatives

The first alternative is the standard library encoding/xml plus a manual walk, or a JSON decoder with struct tags. For a fixed shape of document, that is the right answer and it will be faster, because the compiler generates the parsing code and the traversal is exactly what you wrote.

The second alternative is a scraping library built on goquery, which offers CSS selectors rather than XPath. CSS selectors are the more natural fit for HTML, because HTML is a document about presentation rather than data, and the selector syntax is shorter for the common cases of finding a class or an element by name. The sibling htmlquery package is often used together with it, with htmlquery doing the query and this package providing the XPath capability underneath.

The third alternative is a full browser automation stack, which brings a real rendering engine and the ability to run JavaScript on the page. That is what you need when the content is generated client-side, and it is enormously more expensive than parsing HTML for a page that ships its content in the markup.

What XPath itself brings, and what the other options do not, is the ability to express a path with conditions along the way, to select attributes as well as elements, to navigate upwards with parent and ancestor axes, to select by position, and to compare values. For example, finding every link whose text contains a phrase, or every table cell in a row that also has a certain class, is an XPath expression and a small amount of Go, and it is awkward to express with CSS selectors or with a struct decode.

The comparison with the Java and.NET XPath libraries is the one that shows up when a team is porting. Those have decades of accumulated coverage of the specification, so an expression that works there may not work here. The function table in the README is the honest way to find out before you port.

The decision, in short, is whether you need the expressive path language. If you need to select by attribute value, by position, or relative to an ancestor, XPath is the compact way to say it, and this package is a maintained Go implementation of it with query packages for the three document types you are likely to meet.

Editorial conclusion

Adopt antchfx/xpath if you need XPath evaluation in Go, or one of the three query packages built on it for scraping HTML, reading XML or navigating JSON. Skip it if your documents are small and a struct decode or a JSON path library would do the job more simply. Verify first by running one of the sibling query packages against a document you know well, because the axes and function coverage are partial and you want to see the boundary before you depend on it.

Frequently asked questions

What is the difference between antchfx/xpath and htmlquery, xmlquery and jsonquery?

antchfx/xpath is the expression evaluation engine. htmlquery, xmlquery and jsonquery are the separate query packages built on it, one for each document type, since HTML, XML and JSON need different parsers and different node models.

Which XPath features does antchfx/xpath support?

The basic patterns, the node axes including child, descendant, attribute, sibling, parent, ancestor and self with their shorthand forms, and expressions with comparisons, arithmetic, boolean operators and function calls. The README includes a function table marking each one supported or not, and says the basic patterns cover 90 percent of the cases most stylesheets need.

How is antchfx/xpath licensed and how often is it released?

It is MIT licensed, hosted in the antchfx organisation, and released frequently, with v1.3.7, v1.3.8 and v1.3.9 published between July and October 2026. The repository publishes coverage and build status badges alongside the Go Report Card.

What are the practical limitations of using XPath on HTML with this package?

XPath was designed for well formed XML, while HTML is recovered by a lenient parser, so the tree can have nodes in places the source did not imply and an expression that works on XML can surprise on HTML. Function coverage is also partial, as the README's tick and cross table shows, so expressions are best kept to the basic patterns unless the specific function has been checked.

When should I use XPath in Go rather than CSS selectors or struct decoding?

When you need to select by attribute value, by position, or relative to an ancestor, which is awkward to express with CSS selectors or a struct decode. For a fixed document shape, decoding into structs is faster and simpler, and for client-rendered content a full browser stack is the only option that works at all.

Official sources

  1. antchfx/xpath on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/antchfx-xpath.svg)](https://hysenlabs.com/projects/antchfx-xpath)

Was this useful?