1package xmlstruct 2 3import ( 4 "encoding/xml" 5 "fmt" 6 "reflect" 7 "strings" 8 9 "github.com/ChrisTrenkamp/goxpath/tree" 10) 11 12type XMLEle struct { 13 Val reflect.Value 14 pos int 15 prnt tree.Elem 16 prntTag string 17} 18 19func (x *XMLEle) ResValue() string { 20 if x.Val.Kind() != reflect.Struct { 21 return fmt.Sprintf("%v", x.Val.Interface()) 22 } 23 24 ret := "" 25 for _, i := range x.GetChildren() { 26 switch i.GetNodeType() { 27 case tree.NtChd, tree.NtElem, tree.NtRoot: 28 ret += i.ResValue() 29 } 30 } 31 return ret 32} 33 34func (x *XMLEle) Pos() int { 35 return x.pos 36} 37 38func (x *XMLEle) GetToken() xml.Token { 39 ret := xml.StartElement{} 40 41 if x.prntTag != "" { 42 ret.Name = getTagInfo(x.prntTag).name 43 } else { 44 ret.Name = getXMLName(x.Val) 45 } 46 47 return ret 48} 49 50func (x *XMLEle) GetParent() tree.Elem { 51 return x.prnt 52} 53 54func (x *XMLEle) GetNodeType() tree.NodeType { 55 return tree.NtElem 56} 57 58func (x *XMLEle) GetChildren() []tree.Node { 59 n, _ := getChildren(x, x.Val, x.pos, false) 60 return n 61} 62 63func (x *XMLEle) GetAttrs() []tree.Node { 64 n, _ := getChildren(x, x.Val, x.pos, true) 65 return n 66} 67 68type tagInfo struct { 69 name xml.Name 70 attr bool 71 cdata bool 72 comment bool 73} 74 75func getTagInfo(tag string) (ret tagInfo) { 76 spl := strings.Split(tag, ",") 77 name := strings.Split(spl[0], " ") 78 if len(spl) >= 2 { 79 for i := 1; i < len(spl); i++ { 80 if spl[i] == "chardata" || spl[i] == "cdata" { 81 ret.cdata = true 82 } else if spl[i] == "attr" { 83 ret.attr = true 84 } else if spl[i] == "comment" { 85 ret.comment = true 86 } 87 } 88 } 89 90 if len(name) == 2 { 91 ret.name.Space = name[1] 92 } 93 94 ret.name.Local = name[0] 95 return 96} 97 98func getXMLName(val reflect.Value) xml.Name { 99 n := val.FieldByName("XMLName") 100 zero := reflect.Value{} 101 102 if zero != n { 103 field, _ := val.Type().FieldByName("XMLName") 104 tagInfo := getTagInfo(field.Tag.Get("xml")) 105 if tagInfo.name.Local != "" { 106 return tagInfo.name 107 } 108 109 if name, ok := n.Interface().(xml.Name); ok { 110 return name 111 } 112 } 113 114 return xml.Name{Local: val.Type().Name()} 115} 116 117func getChildren(x *XMLEle, val reflect.Value, pos int, getAttrs bool) ([]tree.Node, int) { 118 if val.Kind() == reflect.Ptr { 119 val = val.Elem() 120 } 121 122 if val.Kind() == reflect.Interface { 123 val = reflect.ValueOf(val.Interface()) 124 } 125 126 if val.Kind() != reflect.Struct { 127 if getAttrs { 128 return []tree.Node{}, x.pos + 1 129 } 130 131 return []tree.Node{&XMLNode{ 132 Val: x.Val, 133 pos: x.pos + 1, 134 prnt: x, 135 nodeType: tree.NtChd, 136 prntTag: "", 137 }}, x.pos + 2 138 } 139 140 ret := make([]tree.Node, 0, val.NumField()) 141 142 for i := 0; i < val.NumField(); i++ { 143 field := val.Field(i) 144 name := val.Type().Field(i).Name 145 146 if val.Type().Field(i).Anonymous { 147 nodes, newPos := getChildren(x, field, pos, getAttrs) 148 ret = append(ret, nodes...) 149 pos = newPos 150 continue 151 } 152 153 tag := val.Type().Field(i).Tag.Get("xml") 154 155 if tag == "-" || name == "XMLName" { 156 continue 157 } 158 159 tagInfo := getTagInfo(tag) 160 161 pos++ 162 163 if tagInfo.attr || tagInfo.cdata || tagInfo.comment { 164 if !getAttrs && tagInfo.attr || getAttrs && !tagInfo.attr { 165 continue 166 } 167 168 child := &XMLNode{ 169 Val: field, 170 pos: pos, 171 prnt: x, 172 prntTag: tag, 173 } 174 175 if tagInfo.attr { 176 child.nodeType = tree.NtAttr 177 } else if tagInfo.cdata { 178 child.nodeType = tree.NtChd 179 } else { 180 child.nodeType = tree.NtComm 181 } 182 183 if tagInfo.name.Local == "" { 184 child.prntTag = name 185 } 186 187 ret = append(ret, child) 188 189 continue 190 } 191 192 if getAttrs { 193 continue 194 } 195 196 child := &XMLEle{Val: field, pos: pos} 197 198 if tag == "" { 199 tag = name 200 } 201 202 child.prntTag = tag 203 ret = append(ret, child) 204 } 205 206 return ret, pos + 1 207} 208