github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/encoding/xml/marshal.go (about) 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 "bufio" 9 "bytes" 10 "encoding" 11 "fmt" 12 "io" 13 "reflect" 14 "strconv" 15 "strings" 16 ) 17 18 const ( 19 // A generic XML header suitable for use with the output of Marshal. 20 // This is not automatically added to any output of this package, 21 // it is provided as a convenience. 22 Header = `<?xml version="1.0" encoding="UTF-8"?>` + "\n" 23 ) 24 25 // Marshal returns the XML encoding of v. 26 // 27 // Marshal handles an array or slice by marshalling each of the elements. 28 // Marshal handles a pointer by marshalling the value it points at or, if the 29 // pointer is nil, by writing nothing. Marshal handles an interface value by 30 // marshalling the value it contains or, if the interface value is nil, by 31 // writing nothing. Marshal handles all other data by writing one or more XML 32 // elements containing the data. 33 // 34 // The name for the XML elements is taken from, in order of preference: 35 // - the tag on the XMLName field, if the data is a struct 36 // - the value of the XMLName field of type xml.Name 37 // - the tag of the struct field used to obtain the data 38 // - the name of the struct field used to obtain the data 39 // - the name of the marshalled type 40 // 41 // The XML element for a struct contains marshalled elements for each of the 42 // exported fields of the struct, with these exceptions: 43 // - the XMLName field, described above, is omitted. 44 // - a field with tag "-" is omitted. 45 // - a field with tag "name,attr" becomes an attribute with 46 // the given name in the XML element. 47 // - a field with tag ",attr" becomes an attribute with the 48 // field name in the XML element. 49 // - a field with tag ",chardata" is written as character data, 50 // not as an XML element. 51 // - a field with tag ",innerxml" is written verbatim, not subject 52 // to the usual marshalling procedure. 53 // - a field with tag ",comment" is written as an XML comment, not 54 // subject to the usual marshalling procedure. It must not contain 55 // the "--" string within it. 56 // - a field with a tag including the "omitempty" option is omitted 57 // if the field value is empty. The empty values are false, 0, any 58 // nil pointer or interface value, and any array, slice, map, or 59 // string of length zero. 60 // - an anonymous struct field is handled as if the fields of its 61 // value were part of the outer struct. 62 // 63 // If a field uses a tag "a>b>c", then the element c will be nested inside 64 // parent elements a and b. Fields that appear next to each other that name 65 // the same parent will be enclosed in one XML element. 66 // 67 // See MarshalIndent for an example. 68 // 69 // Marshal will return an error if asked to marshal a channel, function, or map. 70 func Marshal(v interface{}) ([]byte, error) { 71 var b bytes.Buffer 72 if err := NewEncoder(&b).Encode(v); err != nil { 73 return nil, err 74 } 75 return b.Bytes(), nil 76 } 77 78 // Marshaler is the interface implemented by objects that can marshal 79 // themselves into valid XML elements. 80 // 81 // MarshalXML encodes the receiver as zero or more XML elements. 82 // By convention, arrays or slices are typically encoded as a sequence 83 // of elements, one per entry. 84 // Using start as the element tag is not required, but doing so 85 // will enable Unmarshal to match the XML elements to the correct 86 // struct field. 87 // One common implementation strategy is to construct a separate 88 // value with a layout corresponding to the desired XML and then 89 // to encode it using e.EncodeElement. 90 // Another common strategy is to use repeated calls to e.EncodeToken 91 // to generate the XML output one token at a time. 92 // The sequence of encoded tokens must make up zero or more valid 93 // XML elements. 94 type Marshaler interface { 95 MarshalXML(e *Encoder, start StartElement) error 96 } 97 98 // MarshalerAttr is the interface implemented by objects that can marshal 99 // themselves into valid XML attributes. 100 // 101 // MarshalXMLAttr returns an XML attribute with the encoded value of the receiver. 102 // Using name as the attribute name is not required, but doing so 103 // will enable Unmarshal to match the attribute to the correct 104 // struct field. 105 // If MarshalXMLAttr returns the zero attribute Attr{}, no attribute 106 // will be generated in the output. 107 // MarshalXMLAttr is used only for struct fields with the 108 // "attr" option in the field tag. 109 type MarshalerAttr interface { 110 MarshalXMLAttr(name Name) (Attr, error) 111 } 112 113 // MarshalIndent works like Marshal, but each XML element begins on a new 114 // indented line that starts with prefix and is followed by one or more 115 // copies of indent according to the nesting depth. 116 func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { 117 var b bytes.Buffer 118 enc := NewEncoder(&b) 119 enc.Indent(prefix, indent) 120 if err := enc.Encode(v); err != nil { 121 return nil, err 122 } 123 return b.Bytes(), nil 124 } 125 126 // An Encoder writes XML data to an output stream. 127 type Encoder struct { 128 p printer 129 } 130 131 // NewEncoder returns a new encoder that writes to w. 132 func NewEncoder(w io.Writer) *Encoder { 133 e := &Encoder{printer{Writer: bufio.NewWriter(w)}} 134 e.p.encoder = e 135 return e 136 } 137 138 // Indent sets the encoder to generate XML in which each element 139 // begins on a new indented line that starts with prefix and is followed by 140 // one or more copies of indent according to the nesting depth. 141 func (enc *Encoder) Indent(prefix, indent string) { 142 enc.p.prefix = prefix 143 enc.p.indent = indent 144 } 145 146 // Encode writes the XML encoding of v to the stream. 147 // 148 // See the documentation for Marshal for details about the conversion 149 // of Go values to XML. 150 func (enc *Encoder) Encode(v interface{}) error { 151 err := enc.p.marshalValue(reflect.ValueOf(v), nil, nil) 152 if err != nil { 153 return err 154 } 155 return enc.p.Flush() 156 } 157 158 // EncodeElement writes the XML encoding of v to the stream, 159 // using start as the outermost tag in the encoding. 160 // 161 // See the documentation for Marshal for details about the conversion 162 // of Go values to XML. 163 func (enc *Encoder) EncodeElement(v interface{}, start StartElement) error { 164 err := enc.p.marshalValue(reflect.ValueOf(v), nil, &start) 165 if err != nil { 166 return err 167 } 168 return enc.p.Flush() 169 } 170 171 var ( 172 endComment = []byte("-->") 173 endProcInst = []byte("?>") 174 endDirective = []byte(">") 175 ) 176 177 // EncodeToken writes the given XML token to the stream. 178 // It returns an error if StartElement and EndElement tokens are not properly matched. 179 func (enc *Encoder) EncodeToken(t Token) error { 180 p := &enc.p 181 switch t := t.(type) { 182 case StartElement: 183 if err := p.writeStart(&t); err != nil { 184 return err 185 } 186 case EndElement: 187 if err := p.writeEnd(t.Name); err != nil { 188 return err 189 } 190 case CharData: 191 EscapeText(p, t) 192 case Comment: 193 if bytes.Contains(t, endComment) { 194 return fmt.Errorf("xml: EncodeToken of Comment containing --> marker") 195 } 196 p.WriteString("<!--") 197 p.Write(t) 198 p.WriteString("-->") 199 case ProcInst: 200 if t.Target == "xml" || !isNameString(t.Target) { 201 return fmt.Errorf("xml: EncodeToken of ProcInst with invalid Target") 202 } 203 if bytes.Contains(t.Inst, endProcInst) { 204 return fmt.Errorf("xml: EncodeToken of ProcInst containing ?> marker") 205 } 206 p.WriteString("<?") 207 p.WriteString(t.Target) 208 if len(t.Inst) > 0 { 209 p.WriteByte(' ') 210 p.Write(t.Inst) 211 } 212 p.WriteString("?>") 213 case Directive: 214 if bytes.Contains(t, endDirective) { 215 return fmt.Errorf("xml: EncodeToken of Directive containing > marker") 216 } 217 p.WriteString("<!") 218 p.Write(t) 219 p.WriteString(">") 220 } 221 return p.Flush() 222 } 223 224 type printer struct { 225 *bufio.Writer 226 encoder *Encoder 227 seq int 228 indent string 229 prefix string 230 depth int 231 indentedIn bool 232 putNewline bool 233 attrNS map[string]string // map prefix -> name space 234 attrPrefix map[string]string // map name space -> prefix 235 prefixes []string 236 tags []Name 237 } 238 239 // createAttrPrefix finds the name space prefix attribute to use for the given name space, 240 // defining a new prefix if necessary. It returns the prefix. 241 func (p *printer) createAttrPrefix(url string) string { 242 if prefix := p.attrPrefix[url]; prefix != "" { 243 return prefix 244 } 245 246 // The "http://www.w3.org/XML/1998/namespace" name space is predefined as "xml" 247 // and must be referred to that way. 248 // (The "http://www.w3.org/2000/xmlns/" name space is also predefined as "xmlns", 249 // but users should not be trying to use that one directly - that's our job.) 250 if url == xmlURL { 251 return "xml" 252 } 253 254 // Need to define a new name space. 255 if p.attrPrefix == nil { 256 p.attrPrefix = make(map[string]string) 257 p.attrNS = make(map[string]string) 258 } 259 260 // Pick a name. We try to use the final element of the path 261 // but fall back to _. 262 prefix := strings.TrimRight(url, "/") 263 if i := strings.LastIndex(prefix, "/"); i >= 0 { 264 prefix = prefix[i+1:] 265 } 266 if prefix == "" || !isName([]byte(prefix)) || strings.Contains(prefix, ":") { 267 prefix = "_" 268 } 269 if strings.HasPrefix(prefix, "xml") { 270 // xmlanything is reserved. 271 prefix = "_" + prefix 272 } 273 if p.attrNS[prefix] != "" { 274 // Name is taken. Find a better one. 275 for p.seq++; ; p.seq++ { 276 if id := prefix + "_" + strconv.Itoa(p.seq); p.attrNS[id] == "" { 277 prefix = id 278 break 279 } 280 } 281 } 282 283 p.attrPrefix[url] = prefix 284 p.attrNS[prefix] = url 285 286 p.WriteString(`xmlns:`) 287 p.WriteString(prefix) 288 p.WriteString(`="`) 289 EscapeText(p, []byte(url)) 290 p.WriteString(`" `) 291 292 p.prefixes = append(p.prefixes, prefix) 293 294 return prefix 295 } 296 297 // deleteAttrPrefix removes an attribute name space prefix. 298 func (p *printer) deleteAttrPrefix(prefix string) { 299 delete(p.attrPrefix, p.attrNS[prefix]) 300 delete(p.attrNS, prefix) 301 } 302 303 func (p *printer) markPrefix() { 304 p.prefixes = append(p.prefixes, "") 305 } 306 307 func (p *printer) popPrefix() { 308 for len(p.prefixes) > 0 { 309 prefix := p.prefixes[len(p.prefixes)-1] 310 p.prefixes = p.prefixes[:len(p.prefixes)-1] 311 if prefix == "" { 312 break 313 } 314 p.deleteAttrPrefix(prefix) 315 } 316 } 317 318 var ( 319 marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem() 320 marshalerAttrType = reflect.TypeOf((*MarshalerAttr)(nil)).Elem() 321 textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() 322 ) 323 324 // marshalValue writes one or more XML elements representing val. 325 // If val was obtained from a struct field, finfo must have its details. 326 func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplate *StartElement) error { 327 if startTemplate != nil && startTemplate.Name.Local == "" { 328 return fmt.Errorf("xml: EncodeElement of StartElement with missing name") 329 } 330 331 if !val.IsValid() { 332 return nil 333 } 334 if finfo != nil && finfo.flags&fOmitEmpty != 0 && isEmptyValue(val) { 335 return nil 336 } 337 338 kind := val.Kind() 339 typ := val.Type() 340 341 // Drill into pointers/interfaces 342 if kind == reflect.Ptr || kind == reflect.Interface { 343 if val.IsNil() { 344 return nil 345 } 346 val = val.Elem() 347 typ = val.Type() 348 } 349 350 // Check for marshaler. 351 if val.CanInterface() && typ.Implements(marshalerType) { 352 return p.marshalInterface(val.Interface().(Marshaler), defaultStart(typ, finfo, startTemplate)) 353 } 354 if val.CanAddr() { 355 pv := val.Addr() 356 if pv.CanInterface() && pv.Type().Implements(marshalerType) { 357 return p.marshalInterface(pv.Interface().(Marshaler), defaultStart(pv.Type(), finfo, startTemplate)) 358 } 359 } 360 361 // Check for text marshaler. 362 if val.CanInterface() && typ.Implements(textMarshalerType) { 363 return p.marshalTextInterface(val.Interface().(encoding.TextMarshaler), defaultStart(typ, finfo, startTemplate)) 364 } 365 if val.CanAddr() { 366 pv := val.Addr() 367 if pv.CanInterface() && pv.Type().Implements(textMarshalerType) { 368 return p.marshalTextInterface(pv.Interface().(encoding.TextMarshaler), defaultStart(pv.Type(), finfo, startTemplate)) 369 } 370 } 371 372 // Slices and arrays iterate over the elements. They do not have an enclosing tag. 373 if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 { 374 for i, n := 0, val.Len(); i < n; i++ { 375 if err := p.marshalValue(val.Index(i), finfo, startTemplate); err != nil { 376 return err 377 } 378 } 379 return nil 380 } 381 382 tinfo, err := getTypeInfo(typ) 383 if err != nil { 384 return err 385 } 386 387 // Create start element. 388 // Precedence for the XML element name is: 389 // 0. startTemplate 390 // 1. XMLName field in underlying struct; 391 // 2. field name/tag in the struct field; and 392 // 3. type name 393 var start StartElement 394 395 if startTemplate != nil { 396 start.Name = startTemplate.Name 397 start.Attr = append(start.Attr, startTemplate.Attr...) 398 } else if tinfo.xmlname != nil { 399 xmlname := tinfo.xmlname 400 if xmlname.name != "" { 401 start.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name 402 } else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" { 403 start.Name = v 404 } 405 } 406 if start.Name.Local == "" && finfo != nil { 407 start.Name.Space, start.Name.Local = finfo.xmlns, finfo.name 408 } 409 if start.Name.Local == "" { 410 name := typ.Name() 411 if name == "" { 412 return &UnsupportedTypeError{typ} 413 } 414 start.Name.Local = name 415 } 416 417 // Attributes 418 for i := range tinfo.fields { 419 finfo := &tinfo.fields[i] 420 if finfo.flags&fAttr == 0 { 421 continue 422 } 423 fv := finfo.value(val) 424 name := Name{Space: finfo.xmlns, Local: finfo.name} 425 426 if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) { 427 continue 428 } 429 430 if fv.Kind() == reflect.Interface && fv.IsNil() { 431 continue 432 } 433 434 if fv.CanInterface() && fv.Type().Implements(marshalerAttrType) { 435 attr, err := fv.Interface().(MarshalerAttr).MarshalXMLAttr(name) 436 if err != nil { 437 return err 438 } 439 if attr.Name.Local != "" { 440 start.Attr = append(start.Attr, attr) 441 } 442 continue 443 } 444 445 if fv.CanAddr() { 446 pv := fv.Addr() 447 if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) { 448 attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name) 449 if err != nil { 450 return err 451 } 452 if attr.Name.Local != "" { 453 start.Attr = append(start.Attr, attr) 454 } 455 continue 456 } 457 } 458 459 if fv.CanInterface() && fv.Type().Implements(textMarshalerType) { 460 text, err := fv.Interface().(encoding.TextMarshaler).MarshalText() 461 if err != nil { 462 return err 463 } 464 start.Attr = append(start.Attr, Attr{name, string(text)}) 465 continue 466 } 467 468 if fv.CanAddr() { 469 pv := fv.Addr() 470 if pv.CanInterface() && pv.Type().Implements(textMarshalerType) { 471 text, err := pv.Interface().(encoding.TextMarshaler).MarshalText() 472 if err != nil { 473 return err 474 } 475 start.Attr = append(start.Attr, Attr{name, string(text)}) 476 continue 477 } 478 } 479 480 // Dereference or skip nil pointer, interface values. 481 switch fv.Kind() { 482 case reflect.Ptr, reflect.Interface: 483 if fv.IsNil() { 484 continue 485 } 486 fv = fv.Elem() 487 } 488 489 s, b, err := p.marshalSimple(fv.Type(), fv) 490 if err != nil { 491 return err 492 } 493 if b != nil { 494 s = string(b) 495 } 496 start.Attr = append(start.Attr, Attr{name, s}) 497 } 498 499 if err := p.writeStart(&start); err != nil { 500 return err 501 } 502 503 if val.Kind() == reflect.Struct { 504 err = p.marshalStruct(tinfo, val) 505 } else { 506 s, b, err1 := p.marshalSimple(typ, val) 507 if err1 != nil { 508 err = err1 509 } else if b != nil { 510 EscapeText(p, b) 511 } else { 512 p.EscapeString(s) 513 } 514 } 515 if err != nil { 516 return err 517 } 518 519 if err := p.writeEnd(start.Name); err != nil { 520 return err 521 } 522 523 return p.cachedWriteError() 524 } 525 526 // defaultStart returns the default start element to use, 527 // given the reflect type, field info, and start template. 528 func defaultStart(typ reflect.Type, finfo *fieldInfo, startTemplate *StartElement) StartElement { 529 var start StartElement 530 // Precedence for the XML element name is as above, 531 // except that we do not look inside structs for the first field. 532 if startTemplate != nil { 533 start.Name = startTemplate.Name 534 start.Attr = append(start.Attr, startTemplate.Attr...) 535 } else if finfo != nil && finfo.name != "" { 536 start.Name.Local = finfo.name 537 start.Name.Space = finfo.xmlns 538 } else if typ.Name() != "" { 539 start.Name.Local = typ.Name() 540 } else { 541 // Must be a pointer to a named type, 542 // since it has the Marshaler methods. 543 start.Name.Local = typ.Elem().Name() 544 } 545 return start 546 } 547 548 // marshalInterface marshals a Marshaler interface value. 549 func (p *printer) marshalInterface(val Marshaler, start StartElement) error { 550 // Push a marker onto the tag stack so that MarshalXML 551 // cannot close the XML tags that it did not open. 552 p.tags = append(p.tags, Name{}) 553 n := len(p.tags) 554 555 err := val.MarshalXML(p.encoder, start) 556 if err != nil { 557 return err 558 } 559 560 // Make sure MarshalXML closed all its tags. p.tags[n-1] is the mark. 561 if len(p.tags) > n { 562 return fmt.Errorf("xml: %s.MarshalXML wrote invalid XML: <%s> not closed", receiverType(val), p.tags[len(p.tags)-1].Local) 563 } 564 p.tags = p.tags[:n-1] 565 return nil 566 } 567 568 // marshalTextInterface marshals a TextMarshaler interface value. 569 func (p *printer) marshalTextInterface(val encoding.TextMarshaler, start StartElement) error { 570 if err := p.writeStart(&start); err != nil { 571 return err 572 } 573 text, err := val.MarshalText() 574 if err != nil { 575 return err 576 } 577 EscapeText(p, text) 578 return p.writeEnd(start.Name) 579 } 580 581 // writeStart writes the given start element. 582 func (p *printer) writeStart(start *StartElement) error { 583 if start.Name.Local == "" { 584 return fmt.Errorf("xml: start tag with no name") 585 } 586 587 p.tags = append(p.tags, start.Name) 588 p.markPrefix() 589 590 p.writeIndent(1) 591 p.WriteByte('<') 592 p.WriteString(start.Name.Local) 593 594 if start.Name.Space != "" { 595 p.WriteString(` xmlns="`) 596 p.EscapeString(start.Name.Space) 597 p.WriteByte('"') 598 } 599 600 // Attributes 601 for _, attr := range start.Attr { 602 name := attr.Name 603 if name.Local == "" { 604 continue 605 } 606 p.WriteByte(' ') 607 if name.Space != "" { 608 p.WriteString(p.createAttrPrefix(name.Space)) 609 p.WriteByte(':') 610 } 611 p.WriteString(name.Local) 612 p.WriteString(`="`) 613 p.EscapeString(attr.Value) 614 p.WriteByte('"') 615 } 616 p.WriteByte('>') 617 return nil 618 } 619 620 func (p *printer) writeEnd(name Name) error { 621 if name.Local == "" { 622 return fmt.Errorf("xml: end tag with no name") 623 } 624 if len(p.tags) == 0 || p.tags[len(p.tags)-1].Local == "" { 625 return fmt.Errorf("xml: end tag </%s> without start tag", name.Local) 626 } 627 if top := p.tags[len(p.tags)-1]; top != name { 628 if top.Local != name.Local { 629 return fmt.Errorf("xml: end tag </%s> does not match start tag <%s>", name.Local, top.Local) 630 } 631 return fmt.Errorf("xml: end tag </%s> in namespace %s does not match start tag <%s> in namespace %s", name.Local, name.Space, top.Local, top.Space) 632 } 633 p.tags = p.tags[:len(p.tags)-1] 634 635 p.writeIndent(-1) 636 p.WriteByte('<') 637 p.WriteByte('/') 638 p.WriteString(name.Local) 639 p.WriteByte('>') 640 p.popPrefix() 641 return nil 642 } 643 644 func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) (string, []byte, error) { 645 switch val.Kind() { 646 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 647 return strconv.FormatInt(val.Int(), 10), nil, nil 648 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 649 return strconv.FormatUint(val.Uint(), 10), nil, nil 650 case reflect.Float32, reflect.Float64: 651 return strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()), nil, nil 652 case reflect.String: 653 return val.String(), nil, nil 654 case reflect.Bool: 655 return strconv.FormatBool(val.Bool()), nil, nil 656 case reflect.Array: 657 // will be [...]byte 658 var bytes []byte 659 if val.CanAddr() { 660 bytes = val.Slice(0, val.Len()).Bytes() 661 } else { 662 bytes = make([]byte, val.Len()) 663 reflect.Copy(reflect.ValueOf(bytes), val) 664 } 665 return "", bytes, nil 666 case reflect.Slice: 667 // will be []byte 668 return "", val.Bytes(), nil 669 } 670 return "", nil, &UnsupportedTypeError{typ} 671 } 672 673 var ddBytes = []byte("--") 674 675 func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { 676 s := parentStack{p: p} 677 for i := range tinfo.fields { 678 finfo := &tinfo.fields[i] 679 if finfo.flags&fAttr != 0 { 680 continue 681 } 682 vf := finfo.value(val) 683 684 // Dereference or skip nil pointer, interface values. 685 switch vf.Kind() { 686 case reflect.Ptr, reflect.Interface: 687 if !vf.IsNil() { 688 vf = vf.Elem() 689 } 690 } 691 692 switch finfo.flags & fMode { 693 case fCharData: 694 if vf.CanInterface() && vf.Type().Implements(textMarshalerType) { 695 data, err := vf.Interface().(encoding.TextMarshaler).MarshalText() 696 if err != nil { 697 return err 698 } 699 Escape(p, data) 700 continue 701 } 702 if vf.CanAddr() { 703 pv := vf.Addr() 704 if pv.CanInterface() && pv.Type().Implements(textMarshalerType) { 705 data, err := pv.Interface().(encoding.TextMarshaler).MarshalText() 706 if err != nil { 707 return err 708 } 709 Escape(p, data) 710 continue 711 } 712 } 713 var scratch [64]byte 714 switch vf.Kind() { 715 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 716 Escape(p, strconv.AppendInt(scratch[:0], vf.Int(), 10)) 717 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 718 Escape(p, strconv.AppendUint(scratch[:0], vf.Uint(), 10)) 719 case reflect.Float32, reflect.Float64: 720 Escape(p, strconv.AppendFloat(scratch[:0], vf.Float(), 'g', -1, vf.Type().Bits())) 721 case reflect.Bool: 722 Escape(p, strconv.AppendBool(scratch[:0], vf.Bool())) 723 case reflect.String: 724 if err := EscapeText(p, []byte(vf.String())); err != nil { 725 return err 726 } 727 case reflect.Slice: 728 if elem, ok := vf.Interface().([]byte); ok { 729 if err := EscapeText(p, elem); err != nil { 730 return err 731 } 732 } 733 } 734 continue 735 736 case fComment: 737 k := vf.Kind() 738 if !(k == reflect.String || k == reflect.Slice && vf.Type().Elem().Kind() == reflect.Uint8) { 739 return fmt.Errorf("xml: bad type for comment field of %s", val.Type()) 740 } 741 if vf.Len() == 0 { 742 continue 743 } 744 p.writeIndent(0) 745 p.WriteString("<!--") 746 dashDash := false 747 dashLast := false 748 switch k { 749 case reflect.String: 750 s := vf.String() 751 dashDash = strings.Index(s, "--") >= 0 752 dashLast = s[len(s)-1] == '-' 753 if !dashDash { 754 p.WriteString(s) 755 } 756 case reflect.Slice: 757 b := vf.Bytes() 758 dashDash = bytes.Index(b, ddBytes) >= 0 759 dashLast = b[len(b)-1] == '-' 760 if !dashDash { 761 p.Write(b) 762 } 763 default: 764 panic("can't happen") 765 } 766 if dashDash { 767 return fmt.Errorf(`xml: comments must not contain "--"`) 768 } 769 if dashLast { 770 // "--->" is invalid grammar. Make it "- -->" 771 p.WriteByte(' ') 772 } 773 p.WriteString("-->") 774 continue 775 776 case fInnerXml: 777 iface := vf.Interface() 778 switch raw := iface.(type) { 779 case []byte: 780 p.Write(raw) 781 continue 782 case string: 783 p.WriteString(raw) 784 continue 785 } 786 787 case fElement, fElement | fAny: 788 if err := s.trim(finfo.parents); err != nil { 789 return err 790 } 791 if len(finfo.parents) > len(s.stack) { 792 if vf.Kind() != reflect.Ptr && vf.Kind() != reflect.Interface || !vf.IsNil() { 793 if err := s.push(finfo.parents[len(s.stack):]); err != nil { 794 return err 795 } 796 } 797 } 798 } 799 if err := p.marshalValue(vf, finfo, nil); err != nil { 800 return err 801 } 802 } 803 s.trim(nil) 804 return p.cachedWriteError() 805 } 806 807 // return the bufio Writer's cached write error 808 func (p *printer) cachedWriteError() error { 809 _, err := p.Write(nil) 810 return err 811 } 812 813 func (p *printer) writeIndent(depthDelta int) { 814 if len(p.prefix) == 0 && len(p.indent) == 0 { 815 return 816 } 817 if depthDelta < 0 { 818 p.depth-- 819 if p.indentedIn { 820 p.indentedIn = false 821 return 822 } 823 p.indentedIn = false 824 } 825 if p.putNewline { 826 p.WriteByte('\n') 827 } else { 828 p.putNewline = true 829 } 830 if len(p.prefix) > 0 { 831 p.WriteString(p.prefix) 832 } 833 if len(p.indent) > 0 { 834 for i := 0; i < p.depth; i++ { 835 p.WriteString(p.indent) 836 } 837 } 838 if depthDelta > 0 { 839 p.depth++ 840 p.indentedIn = true 841 } 842 } 843 844 type parentStack struct { 845 p *printer 846 stack []string 847 } 848 849 // trim updates the XML context to match the longest common prefix of the stack 850 // and the given parents. A closing tag will be written for every parent 851 // popped. Passing a zero slice or nil will close all the elements. 852 func (s *parentStack) trim(parents []string) error { 853 split := 0 854 for ; split < len(parents) && split < len(s.stack); split++ { 855 if parents[split] != s.stack[split] { 856 break 857 } 858 } 859 for i := len(s.stack) - 1; i >= split; i-- { 860 if err := s.p.writeEnd(Name{Local: s.stack[i]}); err != nil { 861 return err 862 } 863 } 864 s.stack = parents[:split] 865 return nil 866 } 867 868 // push adds parent elements to the stack and writes open tags. 869 func (s *parentStack) push(parents []string) error { 870 for i := 0; i < len(parents); i++ { 871 if err := s.p.writeStart(&StartElement{Name: Name{Local: parents[i]}}); err != nil { 872 return err 873 } 874 } 875 s.stack = append(s.stack, parents...) 876 return nil 877 } 878 879 // A MarshalXMLError is returned when Marshal encounters a type 880 // that cannot be converted into XML. 881 type UnsupportedTypeError struct { 882 Type reflect.Type 883 } 884 885 func (e *UnsupportedTypeError) Error() string { 886 return "xml: unsupported type: " + e.Type.String() 887 } 888 889 func isEmptyValue(v reflect.Value) bool { 890 switch v.Kind() { 891 case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 892 return v.Len() == 0 893 case reflect.Bool: 894 return !v.Bool() 895 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 896 return v.Int() == 0 897 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 898 return v.Uint() == 0 899 case reflect.Float32, reflect.Float64: 900 return v.Float() == 0 901 case reflect.Interface, reflect.Ptr: 902 return v.IsNil() 903 } 904 return false 905 }