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.
14
// typeInfo holds details for the xml representation of a type.
15
type typeInfo struct {
20
// fieldInfo holds details for the xml representation of a single field.
21
type fieldInfo struct {
32
fElement fieldFlags = 1 << iota
41
fMode = fElement | fAttr | fCharData | fInnerXml | fComment | fAny
44
var tinfoMap = make(map[reflect.Type]*typeInfo)
45
var tinfoLock sync.RWMutex
47
var nameType = reflect.TypeOf(Name{})
49
// getTypeInfo returns the typeInfo structure with details necessary
50
// for marshalling and unmarshalling typ.
51
func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
53
tinfo, ok := tinfoMap[typ]
59
if typ.Kind() == reflect.Struct && typ != nameType {
61
for i := 0; i < n; i++ {
63
if f.PkgPath != "" || f.Tag.Get("xml") == "-" {
64
continue // Private field
67
// For embedded structs, embed its fields.
70
if t.Kind() == reflect.Ptr {
73
if t.Kind() == reflect.Struct {
74
inner, err := getTypeInfo(t)
78
if tinfo.xmlname == nil {
79
tinfo.xmlname = inner.xmlname
81
for _, finfo := range inner.fields {
82
finfo.idx = append([]int{i}, finfo.idx...)
83
if err := addFieldInfo(typ, tinfo, &finfo); err != nil {
91
finfo, err := structFieldInfo(typ, &f)
96
if f.Name == "XMLName" {
101
// Add the field if it doesn't conflict with other fields.
102
if err := addFieldInfo(typ, tinfo, finfo); err != nil {
108
tinfoMap[typ] = tinfo
113
// structFieldInfo builds and returns a fieldInfo for f.
114
func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, error) {
115
finfo := &fieldInfo{idx: f.Index}
117
// Split the tag from the xml namespace if necessary.
118
tag := f.Tag.Get("xml")
119
if i := strings.Index(tag, " "); i >= 0 {
120
finfo.xmlns, tag = tag[:i], tag[i+1:]
124
tokens := strings.Split(tag, ",")
125
if len(tokens) == 1 {
126
finfo.flags = fElement
129
for _, flag := range tokens[1:] {
134
finfo.flags |= fCharData
136
finfo.flags |= fInnerXml
138
finfo.flags |= fComment
142
finfo.flags |= fOmitEmpty
146
// Validate the flags used.
148
switch mode := finfo.flags & fMode; mode {
150
finfo.flags |= fElement
151
case fAttr, fCharData, fInnerXml, fComment, fAny:
152
if f.Name == "XMLName" || tag != "" && mode != fAttr {
156
// This will also catch multiple modes in a single field.
159
if finfo.flags&fMode == fAny {
160
finfo.flags |= fElement
162
if finfo.flags&fOmitEmpty != 0 && finfo.flags&(fElement|fAttr) == 0 {
166
return nil, fmt.Errorf("xml: invalid tag in field %s of type %s: %q",
167
f.Name, typ, f.Tag.Get("xml"))
171
// Use of xmlns without a name is not allowed.
172
if finfo.xmlns != "" && tag == "" {
173
return nil, fmt.Errorf("xml: namespace without name in field %s of type %s: %q",
174
f.Name, typ, f.Tag.Get("xml"))
177
if f.Name == "XMLName" {
178
// The XMLName field records the XML element name. Don't
179
// process it as usual because its name should default to
180
// empty rather than to the field name.
186
// If the name part of the tag is completely empty, get
187
// default from XMLName of underlying struct if feasible,
188
// or field name otherwise.
189
if xmlname := lookupXMLName(f.Type); xmlname != nil {
190
finfo.xmlns, finfo.name = xmlname.xmlns, xmlname.name
197
// Prepare field name and parents.
198
parents := strings.Split(tag, ">")
199
if parents[0] == "" {
202
if parents[len(parents)-1] == "" {
203
return nil, fmt.Errorf("xml: trailing '>' in field %s of type %s", f.Name, typ)
205
finfo.name = parents[len(parents)-1]
206
if len(parents) > 1 {
207
if (finfo.flags & fElement) == 0 {
208
return nil, fmt.Errorf("xml: %s chain not valid with %s flag", tag, strings.Join(tokens[1:], ","))
210
finfo.parents = parents[:len(parents)-1]
213
// If the field type has an XMLName field, the names must match
214
// so that the behavior of both marshalling and unmarshalling
215
// is straightforward and unambiguous.
216
if finfo.flags&fElement != 0 {
218
xmlname := lookupXMLName(ftyp)
219
if xmlname != nil && xmlname.name != finfo.name {
220
return nil, fmt.Errorf("xml: name %q in tag of %s.%s conflicts with name %q in %s.XMLName",
221
finfo.name, typ, f.Name, xmlname.name, ftyp)
227
// lookupXMLName returns the fieldInfo for typ's XMLName field
228
// in case it exists and has a valid xml field tag, otherwise
230
func lookupXMLName(typ reflect.Type) (xmlname *fieldInfo) {
231
for typ.Kind() == reflect.Ptr {
234
if typ.Kind() != reflect.Struct {
237
for i, n := 0, typ.NumField(); i < n; i++ {
239
if f.Name != "XMLName" {
242
finfo, err := structFieldInfo(typ, &f)
243
if finfo.name != "" && err == nil {
246
// Also consider errors as a non-existent field tag
247
// and let getTypeInfo itself report the error.
253
func min(a, b int) int {
260
// addFieldInfo adds finfo to tinfo.fields if there are no
261
// conflicts, or if conflicts arise from previous fields that were
262
// obtained from deeper embedded structures than finfo. In the latter
263
// case, the conflicting entries are dropped.
264
// A conflict occurs when the path (parent + name) to a field is
265
// itself a prefix of another path, or when two paths match exactly.
266
// It is okay for field paths to share a common, shorter prefix.
267
func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error {
270
// First, figure all conflicts. Most working code will have none.
271
for i := range tinfo.fields {
272
oldf := &tinfo.fields[i]
273
if oldf.flags&fMode != newf.flags&fMode {
276
if oldf.xmlns != "" && newf.xmlns != "" && oldf.xmlns != newf.xmlns {
279
minl := min(len(newf.parents), len(oldf.parents))
280
for p := 0; p < minl; p++ {
281
if oldf.parents[p] != newf.parents[p] {
285
if len(oldf.parents) > len(newf.parents) {
286
if oldf.parents[len(newf.parents)] == newf.name {
287
conflicts = append(conflicts, i)
289
} else if len(oldf.parents) < len(newf.parents) {
290
if newf.parents[len(oldf.parents)] == oldf.name {
291
conflicts = append(conflicts, i)
294
if newf.name == oldf.name {
295
conflicts = append(conflicts, i)
299
// Without conflicts, add the new field and return.
300
if conflicts == nil {
301
tinfo.fields = append(tinfo.fields, *newf)
305
// If any conflict is shallower, ignore the new field.
306
// This matches the Go field resolution on embedding.
307
for _, i := range conflicts {
308
if len(tinfo.fields[i].idx) < len(newf.idx) {
313
// Otherwise, if any of them is at the same depth level, it's an error.
314
for _, i := range conflicts {
315
oldf := &tinfo.fields[i]
316
if len(oldf.idx) == len(newf.idx) {
317
f1 := typ.FieldByIndex(oldf.idx)
318
f2 := typ.FieldByIndex(newf.idx)
319
return &TagPathError{typ, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
323
// Otherwise, the new field is shallower, and thus takes precedence,
324
// so drop the conflicting fields from tinfo and append the new one.
325
for c := len(conflicts) - 1; c >= 0; c-- {
327
copy(tinfo.fields[i:], tinfo.fields[i+1:])
328
tinfo.fields = tinfo.fields[:len(tinfo.fields)-1]
330
tinfo.fields = append(tinfo.fields, *newf)
334
// A TagPathError represents an error in the unmarshalling process
335
// caused by the use of field tags with conflicting paths.
336
type TagPathError struct {
342
func (e *TagPathError) Error() string {
343
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)
346
// value returns v's field value corresponding to finfo.
347
// It's equivalent to v.FieldByIndex(finfo.idx), but initializes
348
// and dereferences pointers as necessary.
349
func (finfo *fieldInfo) value(v reflect.Value) reflect.Value {
350
for i, x := range finfo.idx {
353
if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
355
v.Set(reflect.New(v.Type().Elem()))