golang高效XML查询与XPath解析插件库xmlquery的使用
Golang高效XML查询与XPath解析插件库xmlquery的使用
xmlquery是一个用于XML文档的XPath查询包,允许您使用XPath表达式从XML文档中提取数据或评估。
概述
xmlquery具有内置的查询对象缓存功能,可以缓存最近使用的XPATH查询字符串。启用缓存可以避免为每个查询重新编译XPath表达式。
安装
$ go get github.com/antchfx/xmlquery
快速入门
以下是一个基本示例,展示如何使用xmlquery解析XML并执行XPath查询:
import (
"github.com/antchfx/xmlquery"
)
func main(){
s := `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>W3Schools Home Page</title>
<link>https://www.w3schools.com</link>
<description>Free web building tutorials</description>
<item>
<title>RSS Tutorial</title>
<link>https://www.w3schools.com/xml/xml_rss.asp</link>
<description>New RSS tutorial on W3Schools</description>
</item>
<item>
<title>XML Tutorial</title>
<link>https://www.w3schools.com/xml</link>
<description>New XML tutorial on W3Schools</description>
</item>
</channel>
</rss>`
doc, err := xmlquery.Parse(strings.NewReader(s))
if err != nil {
panic(err)
}
channel := xmlquery.FindOne(doc, "//channel")
if n := channel.SelectElement("title"); n != nil {
fmt.Printf("title: %s\n", n.InnerText())
}
if n := channel.SelectElement("link"); n != nil {
fmt.Printf("link: %s\n", n.InnerText())
}
for i, n := range xmlquery.Find(doc, "//item/title") {
fmt.Printf("#%d %s\n", i, n.InnerText())
}
}
基本用法
解析XML
从URL解析XML:
doc, err := xmlquery.LoadURL("http://www.example.com/sitemap.xml")
从字符串解析XML:
s := `<?xml version="1.0" encoding="utf-8"?><rss version="2.0"></rss>`
doc, err := xmlquery.Parse(strings.NewReader(s))
从文件解析XML:
f, err := os.Open("../books.xml")
doc, err := xmlquery.Parse(f)
流式解析XML
对于大型XML文件,可以使用流式解析来节省内存:
f, _ := os.Open("../books.xml")
p, err := xmlquery.CreateStreamParser(f, "/bookstore/book")
for {
n, err := p.Read()
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
fmt.Println(n)
}
带过滤条件的流式解析:
f, _ := os.Open("../books.xml")
p, err := xmlquery.CreateStreamParser(f, "/bookstore/book", "/bookstore/book[price>=10]")
for {
n, err := p.Read()
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
fmt.Println(n)
}
XPath查询示例
查找所有书籍作者
list := xmlquery.Find(doc, "//book//author")
// 或
list := xmlquery.Find(doc, "//author")
查找第二本书
book := xmlquery.FindOne(doc, "//book[2]")
查找最后一本书
book := xmlquery.FindOne(doc, "//book[last()]")
查找所有书籍并只获取id属性
list := xmlquery.Find(doc,"//book/@id")
fmt.Println(list[0].InnerText) // 输出@id值
查找id为bk104的所有书籍
list := xmlquery.Find(doc, "//book[@id='bk104']")
查找价格低于5的所有书籍
list := xmlquery.Find(doc, "//book[price<5]")
计算所有书籍的总价格
expr, err := xpath.Compile("sum(//book/price)")
price := expr.Evaluate(xmlquery.CreateXPathNavigator(doc)).(float64)
fmt.Printf("total price: %f\n", price)
计算书籍数量
expr, err := xpath.Compile("count(//book)")
count := expr.Evaluate(xmlquery.CreateXPathNavigator(doc)).(float64)
高级功能
解析UTF-16 XML文件
f, _ := os.Open(`UTF-16.XML`)
// 将UTF-16 XML转换为UTF-8
utf16ToUtf8Transformer := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder()
utf8Reader := transform.NewReader(f, utf16ToUtf8Transformer)
// 设置`CharsetReader`
options := xmlquery.ParserOptions{
Decoder: &xmlquery.DecoderOptions{
CharsetReader: func(charset string, input io.Reader) (io.Reader, error) {
return input, nil
},
},
}
doc, err := xmlquery.ParseWithOptions(utf8Reader, options)
使用自定义命名空间前缀查询
s := `<?xml version="1.0" encoding="UTF-8"?>
<pd:ProcessDefinition xmlns:pd="http://xmlns.xyz.com/process/2003" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<pd:activity name="Invoke Request-Response Service">
<pd:type>RequestReplyActivity</pd:type>
<pd:resourceType>OpClientReqActivity</pd:resourceType>
<pd:x>300</pd:x>
<pd:y>80</pd:y>
</pd:activity>
</pd:ProcessDefinition>`
nsMap := map[string]string{
"q": "http://xmlns.xyz.com/process/2003",
"r": "http://www.w3.org/1999/XSL/Transform",
"s": "http://www.w3.org/2001/XMLSchema",
}
expr, _ := xpath.CompileWithNS("//q:activity", nsMap)
node := xmlquery.QuerySelector(doc, expr)
创建XML文档
doc := &xmlquery.Node{
Type: xmlquery.DeclarationNode,
Data: "xml",
Attr: []xml.Attr{
xml.Attr{Name: xml.Name{Local: "version"}, Value: "1.0"},
},
}
root := &xmlquery.Node{
Data: "rss",
Type: xmlquery.ElementNode,
}
doc.FirstChild = root
channel := &xmlquery.Node{
Data: "channel",
Type: xmlquery.ElementNode,
}
root.FirstChild = channel
title := &xmlquery.Node{
Data: "title",
Type: xmlquery.ElementNode,
}
title_text := &xmlquery.Node{
Data: "W3Schools Home Page",
Type: xmlquery.TextNode,
}
title.FirstChild = title_text
channel.FirstChild = title
fmt.Println(doc.OutputXML(true))
fmt.Println(doc.OutputXMLWithOptions(WithOutputSelf()))
常见问题
Find() vs QueryAll(),哪个更好?
Find
和QueryAll
都做同样的事情:搜索所有匹配的XML节点。Find
如果提供的XPath查询无效则会panic,而QueryAll
会返回一个错误。
我可以保存查询表达式对象用于下一次查询吗?
是的,可以。我们提供了QuerySelector
和QuerySelectorAll
方法;它们接受您的查询表达式对象。缓存查询表达式对象可以避免重新编译XPath查询表达式,从而提高查询性能。
xmlquery是一个功能强大且高效的XML处理库,特别适合需要复杂查询和大文件处理的场景。通过合理使用其提供的各种功能,可以轻松处理各种XML数据处理需求。
更多关于golang高效XML查询与XPath解析插件库xmlquery的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang高效XML查询与XPath解析插件库xmlquery的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang高效XML查询与XPath解析:xmlquery库使用指南
xmlquery是一个强大的Go语言库,用于高效查询和解析XML文档,支持XPath表达式。下面我将详细介绍它的使用方法。
安装xmlquery
go get github.com/antchfx/xmlquery
基本用法
1. 解析XML文档
package main
import (
"fmt"
"strings"
"github.com/antchfx/xmlquery"
)
func main() {
// 从字符串解析
s := `<?xml version="1.0"?>
<bookstore>
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
</bookstore>`
doc, err := xmlquery.Parse(strings.NewReader(s))
if err != nil {
panic(err)
}
// 从文件解析
// doc, err := xmlquery.LoadDoc("books.xml")
}
2. 使用XPath查询
// 查找所有book节点
books := xmlquery.Find(doc, "//book")
for _, book := range books {
fmt.Printf("Book: %s\n", book.SelectAttr("category"))
}
// 查找特定条件的节点
node := xmlquery.FindOne(doc, "//book[price>35]/title")
if node != nil {
fmt.Println(node.InnerText())
}
3. 遍历节点
// 遍历所有title节点
titles := xmlquery.Find(doc, "//title")
for _, title := range titles {
fmt.Printf("Title: %s (lang=%s)\n",
title.InnerText(),
title.SelectAttr("lang"))
}
// 遍历子节点
root := xmlquery.FindOne(doc, "/bookstore")
for n := root.FirstChild; n != nil; n = n.NextSibling {
if n.Type == xmlquery.ElementNode {
fmt.Println(n.Data)
}
}
高级功能
1. 命名空间处理
// 带命名空间的XML
s := `<root xmlns:foo="http://example.com">
<foo:book>XML Namespace Example</foo:book>
</root>`
doc, _ := xmlquery.Parse(strings.NewReader(s))
// 使用命名空间前缀查询
node := xmlquery.FindOne(doc, "//foo:book")
fmt.Println(node.InnerText())
// 或者使用local-name()函数
node = xmlquery.FindOne(doc, "//*[local-name()='book']")
2. 自定义XPath函数
xmlquery.AddFunction("contains-ci", func(n *xmlquery.Node, args []string) []*xmlquery.Node {
if len(args) != 2 {
return nil
}
var result []*xmlquery.Node
text := strings.ToLower(args[0])
substr := strings.ToLower(args[1])
for _, node := range xmlquery.Find(n.Parent, args[0]) {
if strings.Contains(node.InnerText(), substr) {
result = append(result, node)
}
}
return result
})
// 使用自定义函数
nodes := xmlquery.Find(doc, "//book[contains-ci(title, 'italian')]")
3. 性能优化技巧
// 1. 使用预编译XPath表达式
expr, err := xmlquery.Compile("//book[price>30]")
if err != nil {
panic(err)
}
// 多次使用预编译的表达式
for i := 0; i < 100; i++ {
nodes := expr.Evaluate(xmlquery.CreateXPathNavigator(doc))
// 处理结果...
}
// 2. 使用流式解析大文件
file, err := os.Open("large.xml")
if err != nil {
panic(err)
}
defer file.Close()
// 创建流式解析器
parser := xmlquery.NewStreamParser(file, "//book")
for {
n, err := parser.Read()
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
// 处理每个book节点...
}
完整示例
package main
import (
"fmt"
"log"
"strings"
"github.com/antchfx/xmlquery"
)
func main() {
data := `<?xml version="1.0"?>
<catalog>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
</book>
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
</book>
</catalog>`
doc, err := xmlquery.Parse(strings.NewReader(data))
if err != nil {
log.Fatal(err)
}
// 查询所有价格大于10的书
expr := "//book[price>10]"
books := xmlquery.Find(doc, expr)
fmt.Printf("找到 %d 本价格大于10的书:\n", len(books))
for _, book := range books {
title := xmlquery.FindOne(book, "title")
price := xmlquery.FindOne(book, "price")
fmt.Printf("- %s (%s)\n", title.InnerText(), price.InnerText())
}
// 查询特定ID的书
book := xmlquery.FindOne(doc, "//book[@id='bk102']")
if book != nil {
author := xmlquery.FindOne(book, "author")
fmt.Printf("\nID为bk102的作者是: %s\n", author.InnerText())
}
}
总结
xmlquery库提供了以下优势:
- 完整的XPath 1.0支持
- 高性能的XML解析和查询
- 简单易用的API
- 支持流式处理大文件
- 可扩展的自定义函数
对于需要处理XML的Go项目,xmlquery是一个值得考虑的优秀选择,特别是当你需要XPath查询功能时。