Apply .gitignore rules
This commit is contained in:
@@ -0,0 +1,345 @@
|
||||
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 node,we 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
|
||||
}
|
||||
Reference in New Issue
Block a user