URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [encoding/] [xml/] [marshal.go] - Rev 747
Compare with Previous | Blame | View Log
// Copyright 2011 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package xmlimport ("bufio""bytes""fmt""io""reflect""strconv""strings")const (// A generic XML header suitable for use with the output of Marshal.// This is not automatically added to any output of this package,// it is provided as a convenience.Header = `<?xml version="1.0" encoding="UTF-8"?>` + "\n")// Marshal returns the XML encoding of v.//// Marshal handles an array or slice by marshalling each of the elements.// Marshal handles a pointer by marshalling the value it points at or, if the// pointer is nil, by writing nothing. Marshal handles an interface value by// marshalling the value it contains or, if the interface value is nil, by// writing nothing. Marshal handles all other data by writing one or more XML// elements containing the data.//// The name for the XML elements is taken from, in order of preference:// - 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 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 "-" is omitted.// - 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// the same parent will be enclosed in one XML element. For example://// 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(&Result{Id: 13, FirstName: "John", LastName: "Doe", Age: 42})//// would be marshalled as://// <result>// <person id="13">// <name>// <first>John</first>// <last>Doe</last>// </name>// <age>42</age>// </person>// </result>//// Marshal will return an error if asked to marshal a channel, function, or map.func Marshal(v interface{}) ([]byte, error) {var b bytes.Bufferif err := NewEncoder(&b).Encode(v); err != nil {return nil, err}return b.Bytes(), nil}// An Encoder writes XML data to an output stream.type Encoder struct {printer}// NewEncoder returns a new encoder that writes to w.func NewEncoder(w io.Writer) *Encoder {return &Encoder{printer{bufio.NewWriter(w)}}}// Encode writes the XML encoding of v to the stream.//// See the documentation for Marshal for details about the conversion// of Go values to XML.func (enc *Encoder) Encode(v interface{}) error {err := enc.marshalValue(reflect.ValueOf(v), nil)enc.Flush()return err}type printer struct {*bufio.Writer}func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {if !val.IsValid() {return nil}kind := val.Kind()typ := val.Type()// Drill into pointers/interfacesif kind == reflect.Ptr || kind == reflect.Interface {if val.IsNil() {return nil}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), finfo); err != nil {return err}}return nil}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 namevar xmlns, name stringif tinfo.xmlname != nil {xmlname := tinfo.xmlnameif 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('"')}// Attributesfor i := range tinfo.fields {finfo := &tinfo.fields[i]if finfo.flags&fAttr == 0 {continue}fv := val.FieldByIndex(finfo.idx)switch fv.Kind() {case reflect.String, reflect.Array, reflect.Slice:// TODO: Should we really do this once ,omitempty is in?if fv.Len() == 0 {continue}}p.WriteByte(' ')p.WriteString(finfo.name)p.WriteString(`="`)if err := p.marshalSimple(fv.Type(), fv); err != nil {return err}p.WriteByte('"')}p.WriteByte('>')if val.Kind() == reflect.Struct {err = p.marshalStruct(tinfo, val)} else {err = p.marshalSimple(typ, val)}if err != nil {return err}p.WriteByte('<')p.WriteByte('/')p.WriteString(name)p.WriteByte('>')return nil}func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) error {switch val.Kind() {case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:p.WriteString(strconv.FormatInt(val.Int(), 10))case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:p.WriteString(strconv.FormatUint(val.Uint(), 10))case reflect.Float32, reflect.Float64:p.WriteString(strconv.FormatFloat(val.Float(), 'g', -1, 64))case reflect.String:// TODO: Add EscapeString.Escape(p, []byte(val.String()))case reflect.Bool:p.WriteString(strconv.FormatBool(val.Bool()))case reflect.Array:// will be [...]bytebytes := make([]byte, val.Len())for i := range bytes {bytes[i] = val.Index(i).Interface().(byte)}Escape(p, bytes)case reflect.Slice:// will be []byteEscape(p, val.Bytes())default:return &UnsupportedTypeError{typ}}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)}}continuecase 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 := falsedashLast := falseswitch k {case reflect.String:s := vf.String()dashDash = strings.Index(s, "--") >= 0dashLast = s[len(s)-1] == '-'if !dashDash {p.WriteString(s)}case reflect.Slice:b := vf.Bytes()dashDash = bytes.Index(b, ddBytes) >= 0dashLast = 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("-->")continuecase fInnerXml:iface := vf.Interface()switch raw := iface.(type) {case []byte:p.Write(raw)continuecase 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 {*printerstack []string}// trim updates the XML context to match the longest common prefix of the stack// and the given parents. A closing tag will be written for every parent// popped. Passing a zero slice or nil will close all the elements.func (s *parentStack) trim(parents []string) {split := 0for ; split < len(parents) && split < len(s.stack); split++ {if parents[split] != s.stack[split] {break}}for i := len(s.stack) - 1; i >= split; i-- {s.WriteString("</")s.WriteString(s.stack[i])s.WriteByte('>')}s.stack = parents[:split]}// push adds parent elements to the stack and writes open tags.func (s *parentStack) push(parents []string) {for i := 0; i < len(parents); i++ {s.WriteString("<")s.WriteString(parents[i])s.WriteByte('>')}s.stack = append(s.stack, parents...)}// A MarshalXMLError is returned when Marshal encounters a type// that cannot be converted into XML.type UnsupportedTypeError struct {Type reflect.Type}func (e *UnsupportedTypeError) Error() string {return "xml: unsupported type: " + e.Type.String()}
