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(),哪个更好?

FindQueryAll都做同样的事情:搜索所有匹配的XML节点。Find如果提供的XPath查询无效则会panic,而QueryAll会返回一个错误。

我可以保存查询表达式对象用于下一次查询吗?

是的,可以。我们提供了QuerySelectorQuerySelectorAll方法;它们接受您的查询表达式对象。缓存查询表达式对象可以避免重新编译XPath查询表达式,从而提高查询性能。

xmlquery是一个功能强大且高效的XML处理库,特别适合需要复杂查询和大文件处理的场景。通过合理使用其提供的各种功能,可以轻松处理各种XML数据处理需求。


更多关于golang高效XML查询与XPath解析插件库xmlquery的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于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库提供了以下优势:

  1. 完整的XPath 1.0支持
  2. 高性能的XML解析和查询
  3. 简单易用的API
  4. 支持流式处理大文件
  5. 可扩展的自定义函数

对于需要处理XML的Go项目,xmlquery是一个值得考虑的优秀选择,特别是当你需要XPath查询功能时。

回到顶部