goProject/trunk/goutil/xmlUtil/node.go

346 lines
7.0 KiB
Go
Raw Permalink Normal View History

2025-01-06 16:01:02 +08:00
package xmlUtil
import (
"bytes"
"container/list"
"encoding/xml"
"errors"
"fmt"
"io"
"strings"
)
// A NodeType is the type of a Node.
type NodeType uint
const (
// 文档对象节点(根节点)
DocumentNode NodeType = iota
// 头不声明节点
DeclarationNode
// 元素节点
ElementNode
// 节点文本
TextNode
// 注释
CommentNode
)
// xml节点对象
type Node struct {
Parent, FirstChild, LastChild, PrevSibling, NextSibling *Node
Type NodeType
NodeName string
Namespace string
Attr []xml.Attr
level int // node level in the tree
}
// InnerText returns the text between the start and end tags of the object.
func (n *Node) InnerText() string {
if n.Type == TextNode || n.Type == CommentNode {
return n.NodeName
}
var buf bytes.Buffer
for child := n.FirstChild; child != nil; child = child.NextSibling {
// filt commentnode
if child.Type == CommentNode {
continue
}
buf.WriteString(child.InnerText())
}
return buf.String()
}
func outputXML(buf *bytes.Buffer, n *Node) {
if n.Type == TextNode || n.Type == CommentNode {
buf.WriteString(strings.TrimSpace(n.NodeName))
return
}
buf.WriteString("<" + n.NodeName)
for _, attr := range n.Attr {
if attr.Name.Space != "" {
buf.WriteString(fmt.Sprintf(` %s:%s="%s"`, attr.Name.Space, attr.Name.Local, attr.Value))
} else {
buf.WriteString(fmt.Sprintf(` %s="%s"`, attr.Name.Local, attr.Value))
}
}
buf.WriteString(">")
for child := n.FirstChild; child != nil; child = child.NextSibling {
outputXML(buf, child)
}
buf.WriteString(fmt.Sprintf("</%s>", n.NodeName))
}
// OutputXML returns the text that including tags name.
func (n *Node) OutputXML() string {
var buf bytes.Buffer
outputXML(&buf, n)
return buf.String()
}
// get all children
func (n *Node) Children() []*Node {
childrenList := make([]*Node, 0)
nowChild := n.FirstChild
for {
if nowChild == nil {
break
}
childrenList = append(childrenList, nowChild)
if nowChild == n.LastChild {
break
}
nowChild = nowChild.NextSibling
}
return childrenList
}
// get children len
func (n *Node) ChildrenLen() int {
var count int = 0
nowChild := n.FirstChild
for {
if nowChild == nil {
break
}
count += 1
if nowChild == n.LastChild {
break
}
nowChild = nowChild.NextSibling
}
return count
}
// get all attribute
func (n *Node) ALLAttribute() []xml.Attr {
if n.Attr == nil {
return nil
}
return n.Attr[:]
}
// 获取属性个数
func (n *Node) AttributeLen() int {
if n.Attr == nil {
return 0
}
return len(n.Attr)
}
// 输出所有(主要用于测试)
func (this *Node) OutALL() {
stack := list.New()
tmpItem := this
for {
if tmpItem != nil {
stack.PushBack(tmpItem)
tmpItem = tmpItem.NextSibling
}
break
}
for {
if stack.Len() <= 0 {
break
}
nowNode := stack.Front().Value.(*Node)
stack.Remove(stack.Front())
for _, item := range nowNode.Children() {
stack.PushFront(item)
}
fmt.Println("name:", nowNode.NodeName, " level: ", nowNode.level, " attr:", nowNode.Attr)
}
}
// SelectElements finds child elements with the specified name.
func (n *Node) SelectElements(name string) []*Node {
return Find(n, name)
}
// SelectElements finds child elements with the specified name.
func (n *Node) SelectElement(name string) *Node {
return FindOne(n, name)
}
// SelectAttr returns the attribute value with the specified name.
func (n *Node) SelectAttr(name string) (string, bool) {
var local, space string
local = name
if i := strings.Index(name, ":"); i > 0 {
space = name[:i]
local = name[i+1:]
}
for _, attr := range n.Attr {
if attr.Name.Local == local && attr.Name.Space == space {
return attr.Value, true
}
}
return "", false
}
// 给节点添加属性值
func addAttr(n *Node, key, val string) {
var attr xml.Attr
if i := strings.Index(key, ":"); i > 0 {
attr = xml.Attr{
Name: xml.Name{Space: key[:i], Local: key[i+1:]},
Value: val,
}
} else {
attr = xml.Attr{
Name: xml.Name{Local: key},
Value: val,
}
}
n.Attr = append(n.Attr, attr)
}
// 给节点添加子节点
func addChild(parent, n *Node) {
n.Parent = parent
if parent.FirstChild == nil {
parent.FirstChild = n
} else {
parent.LastChild.NextSibling = n
n.PrevSibling = parent.LastChild
}
parent.LastChild = n
}
// 给节点添加兄弟节点
func addSibling(sibling, n *Node) {
n.Parent = sibling.Parent
sibling.NextSibling = n
n.PrevSibling = sibling
if sibling.Parent != nil {
sibling.Parent.LastChild = n
}
}
// 从reader里面加载xml文档
func LoadFromReader(r io.Reader) (*Node, error) {
var (
decoder = xml.NewDecoder(r) //// xml解码对象
doc = &Node{Type: DocumentNode}
level = 0
declared = false
)
var prev *Node = doc
for {
tok, err := decoder.Token()
switch {
case err == io.EOF:
goto quit
case err != nil:
return nil, err
}
switch tok := tok.(type) {
case xml.StartElement:
//if !declared { // if have no xml nodewe also need add children
// return nil, errors.New("xml: document is invalid")
//}
// if there is no xml node.then create it
if declared == false {
level++
tmpNode := &Node{Type: DeclarationNode, level: level}
addAttr(tmpNode, "version", "1.0")
addAttr(tmpNode, "encoding", "UTF-8")
declared = true
if level == prev.level {
addSibling(prev, tmpNode)
} else if level > prev.level {
addChild(prev, tmpNode)
}
prev = tmpNode
}
node := &Node{
Type: ElementNode,
NodeName: tok.Name.Local,
Namespace: tok.Name.Space,
Attr: tok.Attr,
level: level,
}
//fmt.Println(fmt.Sprintf("start > %s : %d", node.Data, level))
if level == prev.level {
addSibling(prev, node)
} else if level > prev.level {
addChild(prev, node)
} else if level < prev.level {
for i := prev.level - level; i > 1; i-- {
prev = prev.Parent
}
addSibling(prev.Parent, node)
}
prev = node
level++
case xml.EndElement:
level--
case xml.CharData:
node := &Node{Type: TextNode, NodeName: string(tok), level: level}
if level == prev.level {
addSibling(prev, node)
} else if level > prev.level {
addChild(prev, node)
}
case xml.Comment:
node := &Node{Type: CommentNode, NodeName: string(tok), level: level}
if level == prev.level {
addSibling(prev, node)
} else if level > prev.level {
addChild(prev, node)
}
case xml.ProcInst: // Processing Instruction
if declared || (!declared && tok.Target != "xml") {
return nil, errors.New("xml: document is invalid")
}
level++
node := &Node{Type: DeclarationNode, level: level}
pairs := strings.Split(string(tok.Inst), " ")
for _, pair := range pairs {
pair = strings.TrimSpace(pair)
if i := strings.Index(pair, "="); i > 0 {
addAttr(node, pair[:i], strings.Trim(pair[i+1:], `"`))
}
}
declared = true
if level == prev.level {
addSibling(prev, node)
} else if level > prev.level {
addChild(prev, node)
}
prev = node
case xml.Directive:
}
}
quit:
return doc, nil
}