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
45 var tinfoMap sync
.Map
// map[reflect.Type]*typeInfo
47 var nameType
= reflect
.TypeOf(Name
{})
49 // getTypeInfo returns the typeInfo structure with details necessary
50 // for marshaling and unmarshaling typ.
51 func getTypeInfo(typ reflect
.Type
) (*typeInfo
, error
) {
52 if ti
, ok
:= tinfoMap
.Load(typ
); ok
{
53 return ti
.(*typeInfo
), nil
57 if typ
.Kind() == reflect
.Struct
&& typ
!= nameType
{
59 for i
:= 0; i
< n
; i
++ {
61 if (f
.PkgPath
!= "" && !f
.Anonymous
) || f
.Tag
.Get("xml") == "-" {
62 continue // Private field
65 // For embedded structs, embed its fields.
68 if t
.Kind() == reflect
.Ptr
{
71 if t
.Kind() == reflect
.Struct
{
72 inner
, err
:= getTypeInfo(t
)
76 if tinfo
.xmlname
== nil {
77 tinfo
.xmlname
= inner
.xmlname
79 for _
, finfo
:= range inner
.fields
{
80 finfo
.idx
= append([]int{i
}, finfo
.idx
...)
81 if err
:= addFieldInfo(typ
, tinfo
, &finfo
); err
!= nil {
89 finfo
, err
:= structFieldInfo(typ
, &f
)
94 if f
.Name
== "XMLName" {
99 // Add the field if it doesn't conflict with other fields.
100 if err
:= addFieldInfo(typ
, tinfo
, finfo
); err
!= nil {
106 ti
, _
:= tinfoMap
.LoadOrStore(typ
, tinfo
)
107 return ti
.(*typeInfo
), nil
110 // structFieldInfo builds and returns a fieldInfo for f.
111 func structFieldInfo(typ reflect
.Type
, f
*reflect
.StructField
) (*fieldInfo
, error
) {
112 finfo
:= &fieldInfo
{idx
: f
.Index
}
114 // Split the tag from the xml namespace if necessary.
115 tag
:= f
.Tag
.Get("xml")
116 if i
:= strings
.Index(tag
, " "); i
>= 0 {
117 finfo
.xmlns
, tag
= tag
[:i
], tag
[i
+1:]
121 tokens
:= strings
.Split(tag
, ",")
122 if len(tokens
) == 1 {
123 finfo
.flags
= fElement
126 for _
, flag
:= range tokens
[1:] {
131 finfo
.flags |
= fCDATA
133 finfo
.flags |
= fCharData
135 finfo
.flags |
= fInnerXml
137 finfo
.flags |
= fComment
141 finfo
.flags |
= fOmitEmpty
145 // Validate the flags used.
147 switch mode
:= finfo
.flags
& fMode
; mode
{
149 finfo
.flags |
= fElement
150 case fAttr
, fCDATA
, fCharData
, fInnerXml
, fComment
, fAny
, fAny | fAttr
:
151 if f
.Name
== "XMLName" || tag
!= "" && mode
!= fAttr
{
155 // This will also catch multiple modes in a single field.
158 if finfo
.flags
&fMode
== fAny
{
159 finfo
.flags |
= fElement
161 if finfo
.flags
&fOmitEmpty
!= 0 && finfo
.flags
&(fElement|fAttr
) == 0 {
165 return nil, fmt
.Errorf("xml: invalid tag in field %s of type %s: %q",
166 f
.Name
, typ
, f
.Tag
.Get("xml"))
170 // Use of xmlns without a name is not allowed.
171 if finfo
.xmlns
!= "" && tag
== "" {
172 return nil, fmt
.Errorf("xml: namespace without name in field %s of type %s: %q",
173 f
.Name
, typ
, f
.Tag
.Get("xml"))
176 if f
.Name
== "XMLName" {
177 // The XMLName field records the XML element name. Don't
178 // process it as usual because its name should default to
179 // empty rather than to the field name.
185 // If the name part of the tag is completely empty, get
186 // default from XMLName of underlying struct if feasible,
187 // or field name otherwise.
188 if xmlname
:= lookupXMLName(f
.Type
); xmlname
!= nil {
189 finfo
.xmlns
, finfo
.name
= xmlname
.xmlns
, xmlname
.name
196 // Prepare field name and parents.
197 parents
:= strings
.Split(tag
, ">")
198 if parents
[0] == "" {
201 if parents
[len(parents
)-1] == "" {
202 return nil, fmt
.Errorf("xml: trailing '>' in field %s of type %s", f
.Name
, typ
)
204 finfo
.name
= parents
[len(parents
)-1]
205 if len(parents
) > 1 {
206 if (finfo
.flags
& fElement
) == 0 {
207 return nil, fmt
.Errorf("xml: %s chain not valid with %s flag", tag
, strings
.Join(tokens
[1:], ","))
209 finfo
.parents
= parents
[:len(parents
)-1]
212 // If the field type has an XMLName field, the names must match
213 // so that the behavior of both marshaling and unmarshaling
214 // is straightforward and unambiguous.
215 if finfo
.flags
&fElement
!= 0 {
217 xmlname
:= lookupXMLName(ftyp
)
218 if xmlname
!= nil && xmlname
.name
!= finfo
.name
{
219 return nil, fmt
.Errorf("xml: name %q in tag of %s.%s conflicts with name %q in %s.XMLName",
220 finfo
.name
, typ
, f
.Name
, xmlname
.name
, ftyp
)
226 // lookupXMLName returns the fieldInfo for typ's XMLName field
227 // in case it exists and has a valid xml field tag, otherwise
229 func lookupXMLName(typ reflect
.Type
) (xmlname
*fieldInfo
) {
230 for typ
.Kind() == reflect
.Ptr
{
233 if typ
.Kind() != reflect
.Struct
{
236 for i
, n
:= 0, typ
.NumField(); i
< n
; i
++ {
238 if f
.Name
!= "XMLName" {
241 finfo
, err
:= structFieldInfo(typ
, &f
)
242 if finfo
.name
!= "" && err
== nil {
245 // Also consider errors as a non-existent field tag
246 // and let getTypeInfo itself report the error.
252 func min(a
, b
int) int {
259 // addFieldInfo adds finfo to tinfo.fields if there are no
260 // conflicts, or if conflicts arise from previous fields that were
261 // obtained from deeper embedded structures than finfo. In the latter
262 // case, the conflicting entries are dropped.
263 // A conflict occurs when the path (parent + name) to a field is
264 // itself a prefix of another path, or when two paths match exactly.
265 // It is okay for field paths to share a common, shorter prefix.
266 func addFieldInfo(typ reflect
.Type
, tinfo
*typeInfo
, newf
*fieldInfo
) error
{
269 // First, figure all conflicts. Most working code will have none.
270 for i
:= range tinfo
.fields
{
271 oldf
:= &tinfo
.fields
[i
]
272 if oldf
.flags
&fMode
!= newf
.flags
&fMode
{
275 if oldf
.xmlns
!= "" && newf
.xmlns
!= "" && oldf
.xmlns
!= newf
.xmlns
{
278 minl
:= min(len(newf
.parents
), len(oldf
.parents
))
279 for p
:= 0; p
< minl
; p
++ {
280 if oldf
.parents
[p
] != newf
.parents
[p
] {
284 if len(oldf
.parents
) > len(newf
.parents
) {
285 if oldf
.parents
[len(newf
.parents
)] == newf
.name
{
286 conflicts
= append(conflicts
, i
)
288 } else if len(oldf
.parents
) < len(newf
.parents
) {
289 if newf
.parents
[len(oldf
.parents
)] == oldf
.name
{
290 conflicts
= append(conflicts
, i
)
293 if newf
.name
== oldf
.name
{
294 conflicts
= append(conflicts
, i
)
298 // Without conflicts, add the new field and return.
299 if conflicts
== nil {
300 tinfo
.fields
= append(tinfo
.fields
, *newf
)
304 // If any conflict is shallower, ignore the new field.
305 // This matches the Go field resolution on embedding.
306 for _
, i
:= range conflicts
{
307 if len(tinfo
.fields
[i
].idx
) < len(newf
.idx
) {
312 // Otherwise, if any of them is at the same depth level, it's an error.
313 for _
, i
:= range conflicts
{
314 oldf
:= &tinfo
.fields
[i
]
315 if len(oldf
.idx
) == len(newf
.idx
) {
316 f1
:= typ
.FieldByIndex(oldf
.idx
)
317 f2
:= typ
.FieldByIndex(newf
.idx
)
318 return &TagPathError
{typ
, f1
.Name
, f1
.Tag
.Get("xml"), f2
.Name
, f2
.Tag
.Get("xml")}
322 // Otherwise, the new field is shallower, and thus takes precedence,
323 // so drop the conflicting fields from tinfo and append the new one.
324 for c
:= len(conflicts
) - 1; c
>= 0; c
-- {
326 copy(tinfo
.fields
[i
:], tinfo
.fields
[i
+1:])
327 tinfo
.fields
= tinfo
.fields
[:len(tinfo
.fields
)-1]
329 tinfo
.fields
= append(tinfo
.fields
, *newf
)
333 // A TagPathError represents an error in the unmarshaling process
334 // caused by the use of field tags with conflicting paths.
335 type TagPathError
struct {
341 func (e
*TagPathError
) Error() string {
342 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
)
345 // value returns v's field value corresponding to finfo.
346 // It's equivalent to v.FieldByIndex(finfo.idx), but initializes
347 // and dereferences pointers as necessary.
348 func (finfo
*fieldInfo
) value(v reflect
.Value
) reflect
.Value
{
349 for i
, x
:= range finfo
.idx
{
352 if t
.Kind() == reflect
.Ptr
&& t
.Elem().Kind() == reflect
.Struct
{
354 v
.Set(reflect
.New(v
.Type().Elem()))