github.com/fluhus/gostuff@v0.4.1-0.20240331134726-be71864f2b5d/xmlnode/read.go (about) 1 // Package xmlnode provides a hierarchical node representation of XML documents. 2 // This package wraps encoding/xml and can be used instead of it. 3 // 4 // Each node has an underlying concrete type, but calling all functions is 5 // legal. For example, here is how you can traverse the node tree: 6 // 7 // func traverse(n Node) { 8 // // Text() returns an empty string for non-text nodes. 9 // doSomeTextSearch(n.Text()) 10 // 11 // // Children() returns nil for non-parent nodes. 12 // for _, child := range n.Children() { 13 // traverse(child) 14 // } 15 // } 16 package xmlnode 17 18 import ( 19 "encoding/xml" 20 "io" 21 ) 22 23 // ReadAll reads all XML data from the given reader and stores it in a root node. 24 func ReadAll(r io.Reader) (Node, error) { 25 // Create root node. 26 // Starting with Tag instead of Root, to eliminate type checks when referring 27 // to parent nodes during reading. Will be replaced with a Root node at the 28 // end. 29 result := &tag{ 30 nil, 31 "", 32 nil, 33 nil, 34 } 35 dec := xml.NewDecoder(r) 36 37 var t xml.Token 38 var err error 39 current := result 40 41 // Parse tokens. 42 for t, err = dec.Token(); err == nil; t, err = dec.Token() { 43 switch t := t.(type) { 44 case xml.StartElement: 45 // Copy attributes. 46 attrs := make([]*xml.Attr, len(t.Attr)) 47 for i, attr := range t.Attr { 48 attrs[i] = &xml.Attr{Name: attr.Name, Value: attr.Value} 49 } 50 51 // Create child node. 52 child := &tag{ 53 current, 54 t.Name.Local, 55 attrs, 56 nil, 57 } 58 59 current.children = append(current.children, child) 60 current = child 61 62 case xml.EndElement: 63 current = current.Parent().(*tag) 64 65 case xml.CharData: 66 child := &text{ 67 current, 68 string(t), 69 } 70 71 current.children = append(current.children, child) 72 73 case xml.Comment: 74 child := &comment{ 75 current, 76 string(t), 77 } 78 79 current.children = append(current.children, child) 80 81 case xml.ProcInst: 82 child := &procInst{ 83 current, 84 string(t.Target), 85 string(t.Inst), 86 } 87 88 current.children = append(current.children, child) 89 90 case xml.Directive: 91 child := &directive{ 92 current, 93 string(t), 94 } 95 96 current.children = append(current.children, child) 97 } 98 } 99 100 // EOF is ok. 101 if err != io.EOF { 102 return nil, err 103 } 104 105 return &root{result.children}, nil 106 }