diff options
Diffstat (limited to 'libgo/go/encoding/xml/marshal.go')
-rw-r--r-- | libgo/go/encoding/xml/marshal.go | 257 |
1 files changed, 161 insertions, 96 deletions
diff --git a/libgo/go/encoding/xml/marshal.go b/libgo/go/encoding/xml/marshal.go index e94fdbc531f..d25ee30a72b 100644 --- a/libgo/go/encoding/xml/marshal.go +++ b/libgo/go/encoding/xml/marshal.go @@ -6,6 +6,8 @@ package xml import ( "bufio" + "bytes" + "fmt" "io" "reflect" "strconv" @@ -42,20 +44,26 @@ type printer struct { // elements containing the data. // // The name for the XML elements is taken from, in order of preference: -// - the tag on an XMLName field, if the data is a struct -// - the value of an XMLName field of type xml.Name +// - the tag on the XMLName field, if the data is a struct +// - the value of the XMLName field of type xml.Name // - the tag of the struct field used to obtain the data // - the name of the struct field used to obtain the data -// - the name '???'. +// - the name of the marshalled type // // The XML element for a struct contains marshalled elements for each of the // exported fields of the struct, with these exceptions: // - the XMLName field, described above, is omitted. -// - a field with tag "attr" becomes an attribute in the XML element. -// - a field with tag "chardata" is written as character data, -// not as an XML element. -// - a field with tag "innerxml" is written verbatim, -// not subject to the usual marshalling procedure. +// - a field with tag "name,attr" becomes an attribute with +// the given name in the XML element. +// - a field with tag ",attr" becomes an attribute with the +// field name in the in the XML element. +// - a field with tag ",chardata" is written as character data, +// not as an XML element. +// - a field with tag ",innerxml" is written verbatim, not subject +// to the usual marshalling procedure. +// - a field with tag ",comment" is written as an XML comment, not +// subject to the usual marshalling procedure. It must not contain +// the "--" string within it. // // If a field uses a tag "a>b>c", then the element c will be nested inside // parent elements a and b. Fields that appear next to each other that name @@ -63,17 +71,18 @@ type printer struct { // // type Result struct { // XMLName xml.Name `xml:"result"` +// Id int `xml:"id,attr"` // FirstName string `xml:"person>name>first"` // LastName string `xml:"person>name>last"` // Age int `xml:"person>age"` // } // -// xml.Marshal(w, &Result{FirstName: "John", LastName: "Doe", Age: 42}) +// xml.Marshal(w, &Result{Id: 13, FirstName: "John", LastName: "Doe", Age: 42}) // // would be marshalled as: // // <result> -// <person> +// <person id="13"> // <name> // <first>John</first> // <last>Doe</last> @@ -85,12 +94,12 @@ type printer struct { // Marshal will return an error if asked to marshal a channel, function, or map. func Marshal(w io.Writer, v interface{}) (err error) { p := &printer{bufio.NewWriter(w)} - err = p.marshalValue(reflect.ValueOf(v), "???") + err = p.marshalValue(reflect.ValueOf(v), nil) p.Flush() return err } -func (p *printer) marshalValue(val reflect.Value, name string) error { +func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { if !val.IsValid() { return nil } @@ -115,58 +124,75 @@ func (p *printer) marshalValue(val reflect.Value, name string) error { if val.IsNil() { return nil } - return p.marshalValue(val.Elem(), name) + return p.marshalValue(val.Elem(), finfo) } // 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), name); err != nil { + if err := p.marshalValue(val.Index(i), finfo); err != nil { return err } } return nil } - // Find XML name - xmlns := "" - if kind == reflect.Struct { - if f, ok := typ.FieldByName("XMLName"); ok { - if tag := f.Tag.Get("xml"); tag != "" { - if i := strings.Index(tag, " "); i >= 0 { - xmlns, name = tag[:i], tag[i+1:] - } else { - name = tag - } - } else if v, ok := val.FieldByIndex(f.Index).Interface().(Name); ok && v.Local != "" { - xmlns, name = v.Space, v.Local - } + tinfo, err := getTypeInfo(typ) + if err != nil { + return err + } + + // Precedence for the XML element name is: + // 1. XMLName field in underlying struct; + // 2. field name/tag in the struct field; and + // 3. type name + var xmlns, name string + if tinfo.xmlname != nil { + xmlname := tinfo.xmlname + if xmlname.name != "" { + xmlns, name = xmlname.xmlns, xmlname.name + } else if v, ok := val.FieldByIndex(xmlname.idx).Interface().(Name); ok && v.Local != "" { + xmlns, name = v.Space, v.Local + } + } + if name == "" && finfo != nil { + xmlns, name = finfo.xmlns, finfo.name + } + if name == "" { + name = typ.Name() + if name == "" { + return &UnsupportedTypeError{typ} } } p.WriteByte('<') p.WriteString(name) + if xmlns != "" { + p.WriteString(` xmlns="`) + // TODO: EscapeString, to avoid the allocation. + Escape(p, []byte(xmlns)) + p.WriteByte('"') + } + // Attributes - if kind == reflect.Struct { - if len(xmlns) > 0 { - p.WriteString(` xmlns="`) - Escape(p, []byte(xmlns)) - p.WriteByte('"') + for i := range tinfo.fields { + finfo := &tinfo.fields[i] + if finfo.flags&fAttr == 0 { + continue } - - for i, n := 0, typ.NumField(); i < n; i++ { - if f := typ.Field(i); f.PkgPath == "" && f.Tag.Get("xml") == "attr" { - if f.Type.Kind() == reflect.String { - if str := val.Field(i).String(); str != "" { - p.WriteByte(' ') - p.WriteString(strings.ToLower(f.Name)) - p.WriteString(`="`) - Escape(p, []byte(str)) - p.WriteByte('"') - } - } - } + var str string + if fv := val.FieldByIndex(finfo.idx); fv.Kind() == reflect.String { + str = fv.String() + } else { + str = fmt.Sprint(fv.Interface()) + } + if str != "" { + p.WriteByte(' ') + p.WriteString(finfo.name) + p.WriteString(`="`) + Escape(p, []byte(str)) + p.WriteByte('"') } } p.WriteByte('>') @@ -194,58 +220,9 @@ func (p *printer) marshalValue(val reflect.Value, name string) error { bytes := val.Interface().([]byte) Escape(p, bytes) case reflect.Struct: - s := parentStack{printer: p} - for i, n := 0, val.NumField(); i < n; i++ { - if f := typ.Field(i); f.Name != "XMLName" && f.PkgPath == "" { - name := f.Name - vf := val.Field(i) - switch tag := f.Tag.Get("xml"); tag { - case "": - s.trim(nil) - case "chardata": - if tk := f.Type.Kind(); tk == reflect.String { - Escape(p, []byte(vf.String())) - } else if tk == reflect.Slice { - if elem, ok := vf.Interface().([]byte); ok { - Escape(p, elem) - } - } - continue - case "innerxml": - iface := vf.Interface() - switch raw := iface.(type) { - case []byte: - p.Write(raw) - continue - case string: - p.WriteString(raw) - continue - } - case "attr": - continue - default: - parents := strings.Split(tag, ">") - if len(parents) == 1 { - parents, name = nil, tag - } else { - parents, name = parents[:len(parents)-1], parents[len(parents)-1] - if parents[0] == "" { - parents[0] = f.Name - } - } - - s.trim(parents) - if !(vf.Kind() == reflect.Ptr || vf.Kind() == reflect.Interface) || !vf.IsNil() { - s.push(parents[len(s.stack):]) - } - } - - if err := p.marshalValue(vf, name); err != nil { - return err - } - } + if err := p.marshalStruct(tinfo, val); err != nil { + return err } - s.trim(nil) default: return &UnsupportedTypeError{typ} } @@ -258,6 +235,94 @@ func (p *printer) marshalValue(val reflect.Value, name string) error { return nil } +var ddBytes = []byte("--") + +func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { + s := parentStack{printer: p} + for i := range tinfo.fields { + finfo := &tinfo.fields[i] + if finfo.flags&(fAttr|fAny) != 0 { + continue + } + vf := val.FieldByIndex(finfo.idx) + switch finfo.flags & fMode { + case fCharData: + switch vf.Kind() { + case reflect.String: + Escape(p, []byte(vf.String())) + case reflect.Slice: + if elem, ok := vf.Interface().([]byte); ok { + Escape(p, elem) + } + } + continue + + case fComment: + k := vf.Kind() + if !(k == reflect.String || k == reflect.Slice && vf.Type().Elem().Kind() == reflect.Uint8) { + return fmt.Errorf("xml: bad type for comment field of %s", val.Type()) + } + if vf.Len() == 0 { + continue + } + p.WriteString("<!--") + dashDash := false + dashLast := false + switch k { + case reflect.String: + s := vf.String() + dashDash = strings.Index(s, "--") >= 0 + dashLast = s[len(s)-1] == '-' + if !dashDash { + p.WriteString(s) + } + case reflect.Slice: + b := vf.Bytes() + dashDash = bytes.Index(b, ddBytes) >= 0 + dashLast = b[len(b)-1] == '-' + if !dashDash { + p.Write(b) + } + default: + panic("can't happen") + } + if dashDash { + return fmt.Errorf(`xml: comments must not contain "--"`) + } + if dashLast { + // "--->" is invalid grammar. Make it "- -->" + p.WriteByte(' ') + } + p.WriteString("-->") + continue + + case fInnerXml: + iface := vf.Interface() + switch raw := iface.(type) { + case []byte: + p.Write(raw) + continue + case string: + p.WriteString(raw) + continue + } + + case fElement: + s.trim(finfo.parents) + if len(finfo.parents) > len(s.stack) { + if vf.Kind() != reflect.Ptr && vf.Kind() != reflect.Interface || !vf.IsNil() { + s.push(finfo.parents[len(s.stack):]) + } + } + } + if err := p.marshalValue(vf, finfo); err != nil { + return err + } + } + s.trim(nil) + return nil +} + type parentStack struct { *printer stack []string |