You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

372 lines
9.5 KiB

  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package xml
  5. import (
  6. "fmt"
  7. "reflect"
  8. "strings"
  9. "sync"
  10. )
  11. // typeInfo holds details for the xml representation of a type.
  12. type typeInfo struct {
  13. xmlname *fieldInfo
  14. fields []fieldInfo
  15. }
  16. // fieldInfo holds details for the xml representation of a single field.
  17. type fieldInfo struct {
  18. idx []int
  19. name string
  20. xmlns string
  21. flags fieldFlags
  22. parents []string
  23. }
  24. type fieldFlags int
  25. const (
  26. fElement fieldFlags = 1 << iota
  27. fAttr
  28. fCharData
  29. fInnerXml
  30. fComment
  31. fAny
  32. fOmitEmpty
  33. fMode = fElement | fAttr | fCharData | fInnerXml | fComment | fAny
  34. )
  35. var tinfoMap = make(map[reflect.Type]*typeInfo)
  36. var tinfoLock sync.RWMutex
  37. var nameType = reflect.TypeOf(Name{})
  38. // getTypeInfo returns the typeInfo structure with details necessary
  39. // for marshalling and unmarshalling typ.
  40. func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
  41. tinfoLock.RLock()
  42. tinfo, ok := tinfoMap[typ]
  43. tinfoLock.RUnlock()
  44. if ok {
  45. return tinfo, nil
  46. }
  47. tinfo = &typeInfo{}
  48. if typ.Kind() == reflect.Struct && typ != nameType {
  49. n := typ.NumField()
  50. for i := 0; i < n; i++ {
  51. f := typ.Field(i)
  52. if f.PkgPath != "" || f.Tag.Get("xml") == "-" {
  53. continue // Private field
  54. }
  55. // For embedded structs, embed its fields.
  56. if f.Anonymous {
  57. t := f.Type
  58. if t.Kind() == reflect.Ptr {
  59. t = t.Elem()
  60. }
  61. if t.Kind() == reflect.Struct {
  62. inner, err := getTypeInfo(t)
  63. if err != nil {
  64. return nil, err
  65. }
  66. if tinfo.xmlname == nil {
  67. tinfo.xmlname = inner.xmlname
  68. }
  69. for _, finfo := range inner.fields {
  70. finfo.idx = append([]int{i}, finfo.idx...)
  71. if err := addFieldInfo(typ, tinfo, &finfo); err != nil {
  72. return nil, err
  73. }
  74. }
  75. continue
  76. }
  77. }
  78. finfo, err := structFieldInfo(typ, &f)
  79. if err != nil {
  80. return nil, err
  81. }
  82. if f.Name == "XMLName" {
  83. tinfo.xmlname = finfo
  84. continue
  85. }
  86. // Add the field if it doesn't conflict with other fields.
  87. if err := addFieldInfo(typ, tinfo, finfo); err != nil {
  88. return nil, err
  89. }
  90. }
  91. }
  92. tinfoLock.Lock()
  93. tinfoMap[typ] = tinfo
  94. tinfoLock.Unlock()
  95. return tinfo, nil
  96. }
  97. // structFieldInfo builds and returns a fieldInfo for f.
  98. func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, error) {
  99. finfo := &fieldInfo{idx: f.Index}
  100. // Split the tag from the xml namespace if necessary.
  101. tag := f.Tag.Get("xml")
  102. if i := strings.Index(tag, " "); i >= 0 {
  103. finfo.xmlns, tag = tag[:i], tag[i+1:]
  104. }
  105. // Parse flags.
  106. tokens := strings.Split(tag, ",")
  107. if len(tokens) == 1 {
  108. finfo.flags = fElement
  109. } else {
  110. tag = tokens[0]
  111. for _, flag := range tokens[1:] {
  112. switch flag {
  113. case "attr":
  114. finfo.flags |= fAttr
  115. case "chardata":
  116. finfo.flags |= fCharData
  117. case "innerxml":
  118. finfo.flags |= fInnerXml
  119. case "comment":
  120. finfo.flags |= fComment
  121. case "any":
  122. finfo.flags |= fAny
  123. case "omitempty":
  124. finfo.flags |= fOmitEmpty
  125. }
  126. }
  127. // Validate the flags used.
  128. valid := true
  129. switch mode := finfo.flags & fMode; mode {
  130. case 0:
  131. finfo.flags |= fElement
  132. case fAttr, fCharData, fInnerXml, fComment, fAny:
  133. if f.Name == "XMLName" || tag != "" && mode != fAttr {
  134. valid = false
  135. }
  136. default:
  137. // This will also catch multiple modes in a single field.
  138. valid = false
  139. }
  140. if finfo.flags&fMode == fAny {
  141. finfo.flags |= fElement
  142. }
  143. if finfo.flags&fOmitEmpty != 0 && finfo.flags&(fElement|fAttr) == 0 {
  144. valid = false
  145. }
  146. if !valid {
  147. return nil, fmt.Errorf("xml: invalid tag in field %s of type %s: %q",
  148. f.Name, typ, f.Tag.Get("xml"))
  149. }
  150. }
  151. // Use of xmlns without a name is not allowed.
  152. if finfo.xmlns != "" && tag == "" {
  153. return nil, fmt.Errorf("xml: namespace without name in field %s of type %s: %q",
  154. f.Name, typ, f.Tag.Get("xml"))
  155. }
  156. if f.Name == "XMLName" {
  157. // The XMLName field records the XML element name. Don't
  158. // process it as usual because its name should default to
  159. // empty rather than to the field name.
  160. finfo.name = tag
  161. return finfo, nil
  162. }
  163. if tag == "" {
  164. // If the name part of the tag is completely empty, get
  165. // default from XMLName of underlying struct if feasible,
  166. // or field name otherwise.
  167. if xmlname := lookupXMLName(f.Type); xmlname != nil {
  168. finfo.xmlns, finfo.name = xmlname.xmlns, xmlname.name
  169. } else {
  170. finfo.name = f.Name
  171. }
  172. return finfo, nil
  173. }
  174. if finfo.xmlns == "" && finfo.flags&fAttr == 0 {
  175. // If it's an element no namespace specified, get the default
  176. // from the XMLName of enclosing struct if possible.
  177. if xmlname := lookupXMLName(typ); xmlname != nil {
  178. finfo.xmlns = xmlname.xmlns
  179. }
  180. }
  181. // Prepare field name and parents.
  182. parents := strings.Split(tag, ">")
  183. if parents[0] == "" {
  184. parents[0] = f.Name
  185. }
  186. if parents[len(parents)-1] == "" {
  187. return nil, fmt.Errorf("xml: trailing '>' in field %s of type %s", f.Name, typ)
  188. }
  189. finfo.name = parents[len(parents)-1]
  190. if len(parents) > 1 {
  191. if (finfo.flags & fElement) == 0 {
  192. return nil, fmt.Errorf("xml: %s chain not valid with %s flag", tag, strings.Join(tokens[1:], ","))
  193. }
  194. finfo.parents = parents[:len(parents)-1]
  195. }
  196. // If the field type has an XMLName field, the names must match
  197. // so that the behavior of both marshalling and unmarshalling
  198. // is straightforward and unambiguous.
  199. if finfo.flags&fElement != 0 {
  200. ftyp := f.Type
  201. xmlname := lookupXMLName(ftyp)
  202. if xmlname != nil && xmlname.name != finfo.name {
  203. return nil, fmt.Errorf("xml: name %q in tag of %s.%s conflicts with name %q in %s.XMLName",
  204. finfo.name, typ, f.Name, xmlname.name, ftyp)
  205. }
  206. }
  207. return finfo, nil
  208. }
  209. // lookupXMLName returns the fieldInfo for typ's XMLName field
  210. // in case it exists and has a valid xml field tag, otherwise
  211. // it returns nil.
  212. func lookupXMLName(typ reflect.Type) (xmlname *fieldInfo) {
  213. for typ.Kind() == reflect.Ptr {
  214. typ = typ.Elem()
  215. }
  216. if typ.Kind() != reflect.Struct {
  217. return nil
  218. }
  219. for i, n := 0, typ.NumField(); i < n; i++ {
  220. f := typ.Field(i)
  221. if f.Name != "XMLName" {
  222. continue
  223. }
  224. finfo, err := structFieldInfo(typ, &f)
  225. if finfo.name != "" && err == nil {
  226. return finfo
  227. }
  228. // Also consider errors as a non-existent field tag
  229. // and let getTypeInfo itself report the error.
  230. break
  231. }
  232. return nil
  233. }
  234. func min(a, b int) int {
  235. if a <= b {
  236. return a
  237. }
  238. return b
  239. }
  240. // addFieldInfo adds finfo to tinfo.fields if there are no
  241. // conflicts, or if conflicts arise from previous fields that were
  242. // obtained from deeper embedded structures than finfo. In the latter
  243. // case, the conflicting entries are dropped.
  244. // A conflict occurs when the path (parent + name) to a field is
  245. // itself a prefix of another path, or when two paths match exactly.
  246. // It is okay for field paths to share a common, shorter prefix.
  247. func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error {
  248. var conflicts []int
  249. Loop:
  250. // First, figure all conflicts. Most working code will have none.
  251. for i := range tinfo.fields {
  252. oldf := &tinfo.fields[i]
  253. if oldf.flags&fMode != newf.flags&fMode {
  254. continue
  255. }
  256. if oldf.xmlns != "" && newf.xmlns != "" && oldf.xmlns != newf.xmlns {
  257. continue
  258. }
  259. minl := min(len(newf.parents), len(oldf.parents))
  260. for p := 0; p < minl; p++ {
  261. if oldf.parents[p] != newf.parents[p] {
  262. continue Loop
  263. }
  264. }
  265. if len(oldf.parents) > len(newf.parents) {
  266. if oldf.parents[len(newf.parents)] == newf.name {
  267. conflicts = append(conflicts, i)
  268. }
  269. } else if len(oldf.parents) < len(newf.parents) {
  270. if newf.parents[len(oldf.parents)] == oldf.name {
  271. conflicts = append(conflicts, i)
  272. }
  273. } else {
  274. if newf.name == oldf.name {
  275. conflicts = append(conflicts, i)
  276. }
  277. }
  278. }
  279. // Without conflicts, add the new field and return.
  280. if conflicts == nil {
  281. tinfo.fields = append(tinfo.fields, *newf)
  282. return nil
  283. }
  284. // If any conflict is shallower, ignore the new field.
  285. // This matches the Go field resolution on embedding.
  286. for _, i := range conflicts {
  287. if len(tinfo.fields[i].idx) < len(newf.idx) {
  288. return nil
  289. }
  290. }
  291. // Otherwise, if any of them is at the same depth level, it's an error.
  292. for _, i := range conflicts {
  293. oldf := &tinfo.fields[i]
  294. if len(oldf.idx) == len(newf.idx) {
  295. f1 := typ.FieldByIndex(oldf.idx)
  296. f2 := typ.FieldByIndex(newf.idx)
  297. return &TagPathError{typ, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
  298. }
  299. }
  300. // Otherwise, the new field is shallower, and thus takes precedence,
  301. // so drop the conflicting fields from tinfo and append the new one.
  302. for c := len(conflicts) - 1; c >= 0; c-- {
  303. i := conflicts[c]
  304. copy(tinfo.fields[i:], tinfo.fields[i+1:])
  305. tinfo.fields = tinfo.fields[:len(tinfo.fields)-1]
  306. }
  307. tinfo.fields = append(tinfo.fields, *newf)
  308. return nil
  309. }
  310. // A TagPathError represents an error in the unmarshalling process
  311. // caused by the use of field tags with conflicting paths.
  312. type TagPathError struct {
  313. Struct reflect.Type
  314. Field1, Tag1 string
  315. Field2, Tag2 string
  316. }
  317. func (e *TagPathError) Error() string {
  318. return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2)
  319. }
  320. // value returns v's field value corresponding to finfo.
  321. // It's equivalent to v.FieldByIndex(finfo.idx), but initializes
  322. // and dereferences pointers as necessary.
  323. func (finfo *fieldInfo) value(v reflect.Value) reflect.Value {
  324. for i, x := range finfo.idx {
  325. if i > 0 {
  326. t := v.Type()
  327. if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
  328. if v.IsNil() {
  329. v.Set(reflect.New(v.Type().Elem()))
  330. }
  331. v = v.Elem()
  332. }
  333. }
  334. v = v.Field(x)
  335. }
  336. return v
  337. }