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
42 fMode
= fElement | fAttr | fCDATA | fCharData | fInnerXml | fComment | fAny
47 var tinfoMap sync
.Map
// map[reflect.Type]*typeInfo
49 var nameType
= reflect
.TypeOf(Name
{})
51 // getTypeInfo returns the typeInfo structure with details necessary
52 // for marshaling and unmarshaling typ.
53 func getTypeInfo(typ reflect
.Type
) (*typeInfo
, error
) {
54 if ti
, ok
:= tinfoMap
.Load(typ
); ok
{
55 return ti
.(*typeInfo
), nil
59 if typ
.Kind() == reflect
.Struct
&& typ
!= nameType
{
61 for i
:= 0; i
< n
; i
++ {
63 if (f
.PkgPath
!= "" && !f
.Anonymous
) || 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 ti
, _
:= tinfoMap
.LoadOrStore(typ
, tinfo
)
109 return ti
.(*typeInfo
), nil
112 // structFieldInfo builds and returns a fieldInfo for f.
113 func structFieldInfo(typ reflect
.Type
, f
*reflect
.StructField
) (*fieldInfo
, error
) {
114 finfo
:= &fieldInfo
{idx
: f
.Index
}
116 // Split the tag from the xml namespace if necessary.
117 tag
:= f
.Tag
.Get("xml")
118 if i
:= strings
.Index(tag
, " "); i
>= 0 {
119 finfo
.xmlns
, tag
= tag
[:i
], tag
[i
+1:]
123 tokens
:= strings
.Split(tag
, ",")
124 if len(tokens
) == 1 {
125 finfo
.flags
= fElement
128 for _
, flag
:= range tokens
[1:] {
133 finfo
.flags |
= fCDATA
135 finfo
.flags |
= fCharData
137 finfo
.flags |
= fInnerXml
139 finfo
.flags |
= fComment
143 finfo
.flags |
= fOmitEmpty
147 // Validate the flags used.
149 switch mode
:= finfo
.flags
& fMode
; mode
{
151 finfo
.flags |
= fElement
152 case fAttr
, fCDATA
, fCharData
, fInnerXml
, fComment
, fAny
, fAny | fAttr
:
153 if f
.Name
== xmlName || tag
!= "" && mode
!= fAttr
{
157 // This will also catch multiple modes in a single field.
160 if finfo
.flags
&fMode
== fAny
{
161 finfo
.flags |
= fElement
163 if finfo
.flags
&fOmitEmpty
!= 0 && finfo
.flags
&(fElement|fAttr
) == 0 {
167 return nil, fmt
.Errorf("xml: invalid tag in field %s of type %s: %q",
168 f
.Name
, typ
, f
.Tag
.Get("xml"))
172 // Use of xmlns without a name is not allowed.
173 if finfo
.xmlns
!= "" && tag
== "" {
174 return nil, fmt
.Errorf("xml: namespace without name in field %s of type %s: %q",
175 f
.Name
, typ
, f
.Tag
.Get("xml"))
178 if f
.Name
== xmlName
{
179 // The XMLName field records the XML element name. Don't
180 // process it as usual because its name should default to
181 // empty rather than to the field name.
187 // If the name part of the tag is completely empty, get
188 // default from XMLName of underlying struct if feasible,
189 // or field name otherwise.
190 if xmlname
:= lookupXMLName(f
.Type
); xmlname
!= nil {
191 finfo
.xmlns
, finfo
.name
= xmlname
.xmlns
, xmlname
.name
198 // Prepare field name and parents.
199 parents
:= strings
.Split(tag
, ">")
200 if parents
[0] == "" {
203 if parents
[len(parents
)-1] == "" {
204 return nil, fmt
.Errorf("xml: trailing '>' in field %s of type %s", f
.Name
, typ
)
206 finfo
.name
= parents
[len(parents
)-1]
207 if len(parents
) > 1 {
208 if (finfo
.flags
& fElement
) == 0 {
209 return nil, fmt
.Errorf("xml: %s chain not valid with %s flag", tag
, strings
.Join(tokens
[1:], ","))
211 finfo
.parents
= parents
[:len(parents
)-1]
214 // If the field type has an XMLName field, the names must match
215 // so that the behavior of both marshaling and unmarshaling
216 // is straightforward and unambiguous.
217 if finfo
.flags
&fElement
!= 0 {
219 xmlname
:= lookupXMLName(ftyp
)
220 if xmlname
!= nil && xmlname
.name
!= finfo
.name
{
221 return nil, fmt
.Errorf("xml: name %q in tag of %s.%s conflicts with name %q in %s.XMLName",
222 finfo
.name
, typ
, f
.Name
, xmlname
.name
, ftyp
)
228 // lookupXMLName returns the fieldInfo for typ's XMLName field
229 // in case it exists and has a valid xml field tag, otherwise
231 func lookupXMLName(typ reflect
.Type
) (xmlname
*fieldInfo
) {
232 for typ
.Kind() == reflect
.Ptr
{
235 if typ
.Kind() != reflect
.Struct
{
238 for i
, n
:= 0, typ
.NumField(); i
< n
; i
++ {
240 if f
.Name
!= xmlName
{
243 finfo
, err
:= structFieldInfo(typ
, &f
)
244 if err
== nil && finfo
.name
!= "" {
247 // Also consider errors as a non-existent field tag
248 // and let getTypeInfo itself report the error.
254 func min(a
, b
int) int {
261 // addFieldInfo adds finfo to tinfo.fields if there are no
262 // conflicts, or if conflicts arise from previous fields that were
263 // obtained from deeper embedded structures than finfo. In the latter
264 // case, the conflicting entries are dropped.
265 // A conflict occurs when the path (parent + name) to a field is
266 // itself a prefix of another path, or when two paths match exactly.
267 // It is okay for field paths to share a common, shorter prefix.
268 func addFieldInfo(typ reflect
.Type
, tinfo
*typeInfo
, newf
*fieldInfo
) error
{
271 // First, figure all conflicts. Most working code will have none.
272 for i
:= range tinfo
.fields
{
273 oldf
:= &tinfo
.fields
[i
]
274 if oldf
.flags
&fMode
!= newf
.flags
&fMode
{
277 if oldf
.xmlns
!= "" && newf
.xmlns
!= "" && oldf
.xmlns
!= newf
.xmlns
{
280 minl
:= min(len(newf
.parents
), len(oldf
.parents
))
281 for p
:= 0; p
< minl
; p
++ {
282 if oldf
.parents
[p
] != newf
.parents
[p
] {
286 if len(oldf
.parents
) > len(newf
.parents
) {
287 if oldf
.parents
[len(newf
.parents
)] == newf
.name
{
288 conflicts
= append(conflicts
, i
)
290 } else if len(oldf
.parents
) < len(newf
.parents
) {
291 if newf
.parents
[len(oldf
.parents
)] == oldf
.name
{
292 conflicts
= append(conflicts
, i
)
295 if newf
.name
== oldf
.name
{
296 conflicts
= append(conflicts
, i
)
300 // Without conflicts, add the new field and return.
301 if conflicts
== nil {
302 tinfo
.fields
= append(tinfo
.fields
, *newf
)
306 // If any conflict is shallower, ignore the new field.
307 // This matches the Go field resolution on embedding.
308 for _
, i
:= range conflicts
{
309 if len(tinfo
.fields
[i
].idx
) < len(newf
.idx
) {
314 // Otherwise, if any of them is at the same depth level, it's an error.
315 for _
, i
:= range conflicts
{
316 oldf
:= &tinfo
.fields
[i
]
317 if len(oldf
.idx
) == len(newf
.idx
) {
318 f1
:= typ
.FieldByIndex(oldf
.idx
)
319 f2
:= typ
.FieldByIndex(newf
.idx
)
320 return &TagPathError
{typ
, f1
.Name
, f1
.Tag
.Get("xml"), f2
.Name
, f2
.Tag
.Get("xml")}
324 // Otherwise, the new field is shallower, and thus takes precedence,
325 // so drop the conflicting fields from tinfo and append the new one.
326 for c
:= len(conflicts
) - 1; c
>= 0; c
-- {
328 copy(tinfo
.fields
[i
:], tinfo
.fields
[i
+1:])
329 tinfo
.fields
= tinfo
.fields
[:len(tinfo
.fields
)-1]
331 tinfo
.fields
= append(tinfo
.fields
, *newf
)
335 // A TagPathError represents an error in the unmarshaling process
336 // caused by the use of field tags with conflicting paths.
337 type TagPathError
struct {
343 func (e
*TagPathError
) Error() string {
344 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
)
347 // value returns v's field value corresponding to finfo.
348 // It's equivalent to v.FieldByIndex(finfo.idx), but initializes
349 // and dereferences pointers as necessary.
350 func (finfo
*fieldInfo
) value(v reflect
.Value
) reflect
.Value
{
351 for i
, x
:= range finfo
.idx
{
354 if t
.Kind() == reflect
.Ptr
&& t
.Elem().Kind() == reflect
.Struct
{
356 v
.Set(reflect
.New(v
.Type().Elem()))