in webdav/internal/xml/marshal.go [556:716]
func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplate *StartElement) error {
if startTemplate != nil && startTemplate.Name.Local == "" {
return fmt.Errorf("xml: EncodeElement of StartElement with missing name")
}
if !val.IsValid() {
return nil
}
if finfo != nil && finfo.flags&fOmitEmpty != 0 && isEmptyValue(val) {
return nil
}
// Drill into interfaces and pointers.
// This can turn into an infinite loop given a cyclic chain,
// but it matches the Go 1 behavior.
for val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {
if val.IsNil() {
return nil
}
val = val.Elem()
}
kind := val.Kind()
typ := val.Type()
// Check for marshaler.
if val.CanInterface() && typ.Implements(marshalerType) {
return p.marshalInterface(val.Interface().(Marshaler), p.defaultStart(typ, finfo, startTemplate))
}
if val.CanAddr() {
pv := val.Addr()
if pv.CanInterface() && pv.Type().Implements(marshalerType) {
return p.marshalInterface(pv.Interface().(Marshaler), p.defaultStart(pv.Type(), finfo, startTemplate))
}
}
// Check for text marshaler.
if val.CanInterface() && typ.Implements(textMarshalerType) {
return p.marshalTextInterface(val.Interface().(encoding.TextMarshaler), p.defaultStart(typ, finfo, startTemplate))
}
if val.CanAddr() {
pv := val.Addr()
if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
return p.marshalTextInterface(pv.Interface().(encoding.TextMarshaler), p.defaultStart(pv.Type(), finfo, startTemplate))
}
}
// Slices and arrays iterate over the elements. They do not have an enclosing tag.
if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 {
for i, n := 0, val.Len(); i < n; i++ {
if err := p.marshalValue(val.Index(i), finfo, startTemplate); err != nil {
return err
}
}
return nil
}
tinfo, err := getTypeInfo(typ)
if err != nil {
return err
}
// Create start element.
// Precedence for the XML element name is:
// 0. startTemplate
// 1. XMLName field in underlying struct;
// 2. field name/tag in the struct field; and
// 3. type name
var start StartElement
// explicitNS records whether the element's name space has been
// explicitly set (for example an XMLName field).
explicitNS := false
if startTemplate != nil {
start.Name = startTemplate.Name
explicitNS = true
start.Attr = append(start.Attr, startTemplate.Attr...)
} else if tinfo.xmlname != nil {
xmlname := tinfo.xmlname
if xmlname.name != "" {
start.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name
} else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" {
start.Name = v
}
explicitNS = true
}
if start.Name.Local == "" && finfo != nil {
start.Name.Local = finfo.name
if finfo.xmlns != "" {
start.Name.Space = finfo.xmlns
explicitNS = true
}
}
if start.Name.Local == "" {
name := typ.Name()
if name == "" {
return &UnsupportedTypeError{typ}
}
start.Name.Local = name
}
// defaultNS records the default name space as set by a xmlns="..."
// attribute. We don't set p.defaultNS because we want to let
// the attribute writing code (in p.defineNS) be solely responsible
// for maintaining that.
defaultNS := p.defaultNS
// Attributes
for i := range tinfo.fields {
finfo := &tinfo.fields[i]
if finfo.flags&fAttr == 0 {
continue
}
attr, err := p.fieldAttr(finfo, val)
if err != nil {
return err
}
if attr.Name.Local == "" {
continue
}
start.Attr = append(start.Attr, attr)
if attr.Name.Space == "" && attr.Name.Local == "xmlns" {
defaultNS = attr.Value
}
}
if !explicitNS {
// Historic behavior: elements use the default name space
// they are contained in by default.
start.Name.Space = defaultNS
}
// Historic behaviour: an element that's in a namespace sets
// the default namespace for all elements contained within it.
start.setDefaultNamespace()
if err := p.writeStart(&start); err != nil {
return err
}
if val.Kind() == reflect.Struct {
err = p.marshalStruct(tinfo, val)
} else {
s, b, err1 := p.marshalSimple(typ, val)
if err1 != nil {
err = err1
} else if b != nil {
EscapeText(p, b)
} else {
p.EscapeString(s)
}
}
if err != nil {
return err
}
if err := p.writeEnd(start.Name); err != nil {
return err
}
return p.cachedWriteError()
}