...

Source file src/encoding/xml/typeinfo.go

Documentation: encoding/xml

		 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  
		 5  package xml
		 6  
		 7  import (
		 8  	"fmt"
		 9  	"reflect"
		10  	"strings"
		11  	"sync"
		12  )
		13  
		14  // typeInfo holds details for the xml representation of a type.
		15  type typeInfo struct {
		16  	xmlname *fieldInfo
		17  	fields	[]fieldInfo
		18  }
		19  
		20  // fieldInfo holds details for the xml representation of a single field.
		21  type fieldInfo struct {
		22  	idx		 []int
		23  	name		string
		24  	xmlns	 string
		25  	flags	 fieldFlags
		26  	parents []string
		27  }
		28  
		29  type fieldFlags int
		30  
		31  const (
		32  	fElement fieldFlags = 1 << iota
		33  	fAttr
		34  	fCDATA
		35  	fCharData
		36  	fInnerXML
		37  	fComment
		38  	fAny
		39  
		40  	fOmitEmpty
		41  
		42  	fMode = fElement | fAttr | fCDATA | fCharData | fInnerXML | fComment | fAny
		43  
		44  	xmlName = "XMLName"
		45  )
		46  
		47  var tinfoMap sync.Map // map[reflect.Type]*typeInfo
		48  
		49  var nameType = reflect.TypeOf(Name{})
		50  
		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
		56  	}
		57  
		58  	tinfo := &typeInfo{}
		59  	if typ.Kind() == reflect.Struct && typ != nameType {
		60  		n := typ.NumField()
		61  		for i := 0; i < n; i++ {
		62  			f := typ.Field(i)
		63  			if (!f.IsExported() && !f.Anonymous) || f.Tag.Get("xml") == "-" {
		64  				continue // Private field
		65  			}
		66  
		67  			// For embedded structs, embed its fields.
		68  			if f.Anonymous {
		69  				t := f.Type
		70  				if t.Kind() == reflect.Ptr {
		71  					t = t.Elem()
		72  				}
		73  				if t.Kind() == reflect.Struct {
		74  					inner, err := getTypeInfo(t)
		75  					if err != nil {
		76  						return nil, err
		77  					}
		78  					if tinfo.xmlname == nil {
		79  						tinfo.xmlname = inner.xmlname
		80  					}
		81  					for _, finfo := range inner.fields {
		82  						finfo.idx = append([]int{i}, finfo.idx...)
		83  						if err := addFieldInfo(typ, tinfo, &finfo); err != nil {
		84  							return nil, err
		85  						}
		86  					}
		87  					continue
		88  				}
		89  			}
		90  
		91  			finfo, err := structFieldInfo(typ, &f)
		92  			if err != nil {
		93  				return nil, err
		94  			}
		95  
		96  			if f.Name == xmlName {
		97  				tinfo.xmlname = finfo
		98  				continue
		99  			}
	 100  
	 101  			// Add the field if it doesn't conflict with other fields.
	 102  			if err := addFieldInfo(typ, tinfo, finfo); err != nil {
	 103  				return nil, err
	 104  			}
	 105  		}
	 106  	}
	 107  
	 108  	ti, _ := tinfoMap.LoadOrStore(typ, tinfo)
	 109  	return ti.(*typeInfo), nil
	 110  }
	 111  
	 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}
	 115  
	 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:]
	 120  	}
	 121  
	 122  	// Parse flags.
	 123  	tokens := strings.Split(tag, ",")
	 124  	if len(tokens) == 1 {
	 125  		finfo.flags = fElement
	 126  	} else {
	 127  		tag = tokens[0]
	 128  		for _, flag := range tokens[1:] {
	 129  			switch flag {
	 130  			case "attr":
	 131  				finfo.flags |= fAttr
	 132  			case "cdata":
	 133  				finfo.flags |= fCDATA
	 134  			case "chardata":
	 135  				finfo.flags |= fCharData
	 136  			case "innerxml":
	 137  				finfo.flags |= fInnerXML
	 138  			case "comment":
	 139  				finfo.flags |= fComment
	 140  			case "any":
	 141  				finfo.flags |= fAny
	 142  			case "omitempty":
	 143  				finfo.flags |= fOmitEmpty
	 144  			}
	 145  		}
	 146  
	 147  		// Validate the flags used.
	 148  		valid := true
	 149  		switch mode := finfo.flags & fMode; mode {
	 150  		case 0:
	 151  			finfo.flags |= fElement
	 152  		case fAttr, fCDATA, fCharData, fInnerXML, fComment, fAny, fAny | fAttr:
	 153  			if f.Name == xmlName || tag != "" && mode != fAttr {
	 154  				valid = false
	 155  			}
	 156  		default:
	 157  			// This will also catch multiple modes in a single field.
	 158  			valid = false
	 159  		}
	 160  		if finfo.flags&fMode == fAny {
	 161  			finfo.flags |= fElement
	 162  		}
	 163  		if finfo.flags&fOmitEmpty != 0 && finfo.flags&(fElement|fAttr) == 0 {
	 164  			valid = false
	 165  		}
	 166  		if !valid {
	 167  			return nil, fmt.Errorf("xml: invalid tag in field %s of type %s: %q",
	 168  				f.Name, typ, f.Tag.Get("xml"))
	 169  		}
	 170  	}
	 171  
	 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"))
	 176  	}
	 177  
	 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.
	 182  		finfo.name = tag
	 183  		return finfo, nil
	 184  	}
	 185  
	 186  	if tag == "" {
	 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
	 192  		} else {
	 193  			finfo.name = f.Name
	 194  		}
	 195  		return finfo, nil
	 196  	}
	 197  
	 198  	// Prepare field name and parents.
	 199  	parents := strings.Split(tag, ">")
	 200  	if parents[0] == "" {
	 201  		parents[0] = f.Name
	 202  	}
	 203  	if parents[len(parents)-1] == "" {
	 204  		return nil, fmt.Errorf("xml: trailing '>' in field %s of type %s", f.Name, typ)
	 205  	}
	 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:], ","))
	 210  		}
	 211  		finfo.parents = parents[:len(parents)-1]
	 212  	}
	 213  
	 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 {
	 218  		ftyp := f.Type
	 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)
	 223  		}
	 224  	}
	 225  	return finfo, nil
	 226  }
	 227  
	 228  // lookupXMLName returns the fieldInfo for typ's XMLName field
	 229  // in case it exists and has a valid xml field tag, otherwise
	 230  // it returns nil.
	 231  func lookupXMLName(typ reflect.Type) (xmlname *fieldInfo) {
	 232  	for typ.Kind() == reflect.Ptr {
	 233  		typ = typ.Elem()
	 234  	}
	 235  	if typ.Kind() != reflect.Struct {
	 236  		return nil
	 237  	}
	 238  	for i, n := 0, typ.NumField(); i < n; i++ {
	 239  		f := typ.Field(i)
	 240  		if f.Name != xmlName {
	 241  			continue
	 242  		}
	 243  		finfo, err := structFieldInfo(typ, &f)
	 244  		if err == nil && finfo.name != "" {
	 245  			return finfo
	 246  		}
	 247  		// Also consider errors as a non-existent field tag
	 248  		// and let getTypeInfo itself report the error.
	 249  		break
	 250  	}
	 251  	return nil
	 252  }
	 253  
	 254  func min(a, b int) int {
	 255  	if a <= b {
	 256  		return a
	 257  	}
	 258  	return b
	 259  }
	 260  
	 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 {
	 269  	var conflicts []int
	 270  Loop:
	 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 {
	 275  			continue
	 276  		}
	 277  		if oldf.xmlns != "" && newf.xmlns != "" && oldf.xmlns != newf.xmlns {
	 278  			continue
	 279  		}
	 280  		minl := min(len(newf.parents), len(oldf.parents))
	 281  		for p := 0; p < minl; p++ {
	 282  			if oldf.parents[p] != newf.parents[p] {
	 283  				continue Loop
	 284  			}
	 285  		}
	 286  		if len(oldf.parents) > len(newf.parents) {
	 287  			if oldf.parents[len(newf.parents)] == newf.name {
	 288  				conflicts = append(conflicts, i)
	 289  			}
	 290  		} else if len(oldf.parents) < len(newf.parents) {
	 291  			if newf.parents[len(oldf.parents)] == oldf.name {
	 292  				conflicts = append(conflicts, i)
	 293  			}
	 294  		} else {
	 295  			if newf.name == oldf.name {
	 296  				conflicts = append(conflicts, i)
	 297  			}
	 298  		}
	 299  	}
	 300  	// Without conflicts, add the new field and return.
	 301  	if conflicts == nil {
	 302  		tinfo.fields = append(tinfo.fields, *newf)
	 303  		return nil
	 304  	}
	 305  
	 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) {
	 310  			return nil
	 311  		}
	 312  	}
	 313  
	 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")}
	 321  		}
	 322  	}
	 323  
	 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-- {
	 327  		i := conflicts[c]
	 328  		copy(tinfo.fields[i:], tinfo.fields[i+1:])
	 329  		tinfo.fields = tinfo.fields[:len(tinfo.fields)-1]
	 330  	}
	 331  	tinfo.fields = append(tinfo.fields, *newf)
	 332  	return nil
	 333  }
	 334  
	 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 {
	 338  	Struct			 reflect.Type
	 339  	Field1, Tag1 string
	 340  	Field2, Tag2 string
	 341  }
	 342  
	 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)
	 345  }
	 346  
	 347  const (
	 348  	initNilPointers		 = true
	 349  	dontInitNilPointers = false
	 350  )
	 351  
	 352  // value returns v's field value corresponding to finfo.
	 353  // It's equivalent to v.FieldByIndex(finfo.idx), but when passed
	 354  // initNilPointers, it initializes and dereferences pointers as necessary.
	 355  // When passed dontInitNilPointers and a nil pointer is reached, the function
	 356  // returns a zero reflect.Value.
	 357  func (finfo *fieldInfo) value(v reflect.Value, shouldInitNilPointers bool) reflect.Value {
	 358  	for i, x := range finfo.idx {
	 359  		if i > 0 {
	 360  			t := v.Type()
	 361  			if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
	 362  				if v.IsNil() {
	 363  					if !shouldInitNilPointers {
	 364  						return reflect.Value{}
	 365  					}
	 366  					v.Set(reflect.New(v.Type().Elem()))
	 367  				}
	 368  				v = v.Elem()
	 369  			}
	 370  		}
	 371  		v = v.Field(x)
	 372  	}
	 373  	return v
	 374  }
	 375  

View as plain text