package xml
import (
)
const (
Header = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
)
func ( interface{}) ([]byte, error) {
var bytes.Buffer
if := NewEncoder(&).Encode(); != nil {
return nil,
}
return .Bytes(), nil
}
type Marshaler interface {
MarshalXML(e *Encoder, start StartElement) error
}
type MarshalerAttr interface {
MarshalXMLAttr(name Name) (Attr, error)
}
func ( interface{}, , string) ([]byte, error) {
var bytes.Buffer
:= NewEncoder(&)
.Indent(, )
if := .Encode(); != nil {
return nil,
}
return .Bytes(), nil
}
type Encoder struct {
p printer
}
func ( io.Writer) *Encoder {
:= &Encoder{printer{Writer: bufio.NewWriter()}}
.p.encoder =
return
}
func ( *Encoder) (, string) {
.p.prefix =
.p.indent =
}
func ( *Encoder) ( interface{}) error {
:= .p.marshalValue(reflect.ValueOf(), nil, nil)
if != nil {
return
}
return .p.Flush()
}
func ( *Encoder) ( interface{}, StartElement) error {
:= .p.marshalValue(reflect.ValueOf(), nil, &)
if != nil {
return
}
return .p.Flush()
}
var (
begComment = []byte("<!--")
endComment = []byte("-->")
endProcInst = []byte("?>")
)
func ( *Encoder) ( Token) error {
:= &.p
switch t := .(type) {
case StartElement:
if := .writeStart(&); != nil {
return
}
case EndElement:
if := .writeEnd(.Name); != nil {
return
}
case CharData:
escapeText(, , false)
case Comment:
if bytes.Contains(, endComment) {
return fmt.Errorf("xml: EncodeToken of Comment containing --> marker")
}
.WriteString("<!--")
.Write()
.WriteString("-->")
return .cachedWriteError()
case ProcInst:
if .Target == "xml" && .Buffered() != 0 {
return fmt.Errorf("xml: EncodeToken of ProcInst xml target only valid for xml declaration, first token encoded")
}
if !isNameString(.Target) {
return fmt.Errorf("xml: EncodeToken of ProcInst with invalid Target")
}
if bytes.Contains(.Inst, endProcInst) {
return fmt.Errorf("xml: EncodeToken of ProcInst containing ?> marker")
}
.WriteString("<?")
.WriteString(.Target)
if len(.Inst) > 0 {
.WriteByte(' ')
.Write(.Inst)
}
.WriteString("?>")
case Directive:
if !isValidDirective() {
return fmt.Errorf("xml: EncodeToken of Directive containing wrong < or > markers")
}
.WriteString("<!")
.Write()
.WriteString(">")
default:
return fmt.Errorf("xml: EncodeToken of invalid token type")
}
return .cachedWriteError()
}
func ( Directive) bool {
var (
int
uint8
bool
)
for , := range {
switch {
case :
if == '>' {
if := 1 + - len(endComment); >= 0 && bytes.Equal([:+1], endComment) {
= false
}
}
case != 0:
if == {
= 0
}
case == '\'' || == '"':
=
case == '<':
if +len(begComment) < len() && bytes.Equal([:+len(begComment)], begComment) {
= true
} else {
++
}
case == '>':
if == 0 {
return false
}
--
}
}
return == 0 && == 0 && !
}
func ( *Encoder) () error {
return .p.Flush()
}
type printer struct {
*bufio.Writer
encoder *Encoder
seq int
indent string
prefix string
depth int
indentedIn bool
putNewline bool
attrNS map[string]string
attrPrefix map[string]string
prefixes []string
tags []Name
}
func ( *printer) ( string) string {
if := .attrPrefix[]; != "" {
return
}
if == xmlURL {
return xmlPrefix
}
if .attrPrefix == nil {
.attrPrefix = make(map[string]string)
.attrNS = make(map[string]string)
}
:= strings.TrimRight(, "/")
if := strings.LastIndex(, "/"); >= 0 {
= [+1:]
}
if == "" || !isName([]byte()) || strings.Contains(, ":") {
= "_"
}
if len() >= 3 && strings.EqualFold([:3], "xml") {
= "_" +
}
if .attrNS[] != "" {
for .seq++; ; .seq++ {
if := + "_" + strconv.Itoa(.seq); .attrNS[] == "" {
=
break
}
}
}
.attrPrefix[] =
.attrNS[] =
.WriteString(`xmlns:`)
.WriteString()
.WriteString(`="`)
EscapeText(, []byte())
.WriteString(`" `)
.prefixes = append(.prefixes, )
return
}
func ( *printer) ( string) {
delete(.attrPrefix, .attrNS[])
delete(.attrNS, )
}
func ( *printer) () {
.prefixes = append(.prefixes, "")
}
func ( *printer) () {
for len(.prefixes) > 0 {
:= .prefixes[len(.prefixes)-1]
.prefixes = .prefixes[:len(.prefixes)-1]
if == "" {
break
}
.deleteAttrPrefix()
}
}
var (
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
marshalerAttrType = reflect.TypeOf((*MarshalerAttr)(nil)).Elem()
textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
)
func ( *printer) ( reflect.Value, *fieldInfo, *StartElement) error {
if != nil && .Name.Local == "" {
return fmt.Errorf("xml: EncodeElement of StartElement with missing name")
}
if !.IsValid() {
return nil
}
if != nil && .flags&fOmitEmpty != 0 && isEmptyValue() {
return nil
}
for .Kind() == reflect.Interface || .Kind() == reflect.Ptr {
if .IsNil() {
return nil
}
= .Elem()
}
:= .Kind()
:= .Type()
if .CanInterface() && .Implements(marshalerType) {
return .marshalInterface(.Interface().(Marshaler), defaultStart(, , ))
}
if .CanAddr() {
:= .Addr()
if .CanInterface() && .Type().Implements(marshalerType) {
return .marshalInterface(.Interface().(Marshaler), defaultStart(.Type(), , ))
}
}
if .CanInterface() && .Implements(textMarshalerType) {
return .marshalTextInterface(.Interface().(encoding.TextMarshaler), defaultStart(, , ))
}
if .CanAddr() {
:= .Addr()
if .CanInterface() && .Type().Implements(textMarshalerType) {
return .marshalTextInterface(.Interface().(encoding.TextMarshaler), defaultStart(.Type(), , ))
}
}
if ( == reflect.Slice || == reflect.Array) && .Elem().Kind() != reflect.Uint8 {
for , := 0, .Len(); < ; ++ {
if := .(.Index(), , ); != nil {
return
}
}
return nil
}
, := getTypeInfo()
if != nil {
return
}
var StartElement
if != nil {
.Name = .Name
.Attr = append(.Attr, .Attr...)
} else if .xmlname != nil {
:= .xmlname
if .name != "" {
.Name.Space, .Name.Local = .xmlns, .name
} else {
:= .value(, dontInitNilPointers)
if , := .Interface().(Name); && .Local != "" {
.Name =
}
}
}
if .Name.Local == "" && != nil {
.Name.Space, .Name.Local = .xmlns, .name
}
if .Name.Local == "" {
:= .Name()
if == "" {
return &UnsupportedTypeError{}
}
.Name.Local =
}
for := range .fields {
:= &.fields[]
if .flags&fAttr == 0 {
continue
}
:= .value(, dontInitNilPointers)
if .flags&fOmitEmpty != 0 && isEmptyValue() {
continue
}
if .Kind() == reflect.Interface && .IsNil() {
continue
}
:= Name{Space: .xmlns, Local: .name}
if := .marshalAttr(&, , ); != nil {
return
}
}
if := .writeStart(&); != nil {
return
}
if .Kind() == reflect.Struct {
= .marshalStruct(, )
} else {
, , := .marshalSimple(, )
if != nil {
=
} else if != nil {
EscapeText(, )
} else {
.EscapeString()
}
}
if != nil {
return
}
if := .writeEnd(.Name); != nil {
return
}
return .cachedWriteError()
}
func ( *printer) ( *StartElement, Name, reflect.Value) error {
if .CanInterface() && .Type().Implements(marshalerAttrType) {
, := .Interface().(MarshalerAttr).MarshalXMLAttr()
if != nil {
return
}
if .Name.Local != "" {
.Attr = append(.Attr, )
}
return nil
}
if .CanAddr() {
:= .Addr()
if .CanInterface() && .Type().Implements(marshalerAttrType) {
, := .Interface().(MarshalerAttr).MarshalXMLAttr()
if != nil {
return
}
if .Name.Local != "" {
.Attr = append(.Attr, )
}
return nil
}
}
if .CanInterface() && .Type().Implements(textMarshalerType) {
, := .Interface().(encoding.TextMarshaler).MarshalText()
if != nil {
return
}
.Attr = append(.Attr, Attr{, string()})
return nil
}
if .CanAddr() {
:= .Addr()
if .CanInterface() && .Type().Implements(textMarshalerType) {
, := .Interface().(encoding.TextMarshaler).MarshalText()
if != nil {
return
}
.Attr = append(.Attr, Attr{, string()})
return nil
}
}
switch .Kind() {
case reflect.Ptr, reflect.Interface:
if .IsNil() {
return nil
}
= .Elem()
}
if .Kind() == reflect.Slice && .Type().Elem().Kind() != reflect.Uint8 {
:= .Len()
for := 0; < ; ++ {
if := .(, , .Index()); != nil {
return
}
}
return nil
}
if .Type() == attrType {
.Attr = append(.Attr, .Interface().(Attr))
return nil
}
, , := .marshalSimple(.Type(), )
if != nil {
return
}
if != nil {
= string()
}
.Attr = append(.Attr, Attr{, })
return nil
}
func ( reflect.Type, *fieldInfo, *StartElement) StartElement {
var StartElement
if != nil {
.Name = .Name
.Attr = append(.Attr, .Attr...)
} else if != nil && .name != "" {
.Name.Local = .name
.Name.Space = .xmlns
} else if .Name() != "" {
.Name.Local = .Name()
} else {
.Name.Local = .Elem().Name()
}
return
}
func ( *printer) ( Marshaler, StartElement) error {
.tags = append(.tags, Name{})
:= len(.tags)
:= .MarshalXML(.encoder, )
if != nil {
return
}
if len(.tags) > {
return fmt.Errorf("xml: %s.MarshalXML wrote invalid XML: <%s> not closed", receiverType(), .tags[len(.tags)-1].Local)
}
.tags = .tags[:-1]
return nil
}
func ( *printer) ( encoding.TextMarshaler, StartElement) error {
if := .writeStart(&); != nil {
return
}
, := .MarshalText()
if != nil {
return
}
EscapeText(, )
return .writeEnd(.Name)
}
func ( *printer) ( *StartElement) error {
if .Name.Local == "" {
return fmt.Errorf("xml: start tag with no name")
}
.tags = append(.tags, .Name)
.markPrefix()
.writeIndent(1)
.WriteByte('<')
.WriteString(.Name.Local)
if .Name.Space != "" {
.WriteString(` xmlns="`)
.EscapeString(.Name.Space)
.WriteByte('"')
}
for , := range .Attr {
:= .Name
if .Local == "" {
continue
}
.WriteByte(' ')
if .Space != "" {
.WriteString(.createAttrPrefix(.Space))
.WriteByte(':')
}
.WriteString(.Local)
.WriteString(`="`)
.EscapeString(.Value)
.WriteByte('"')
}
.WriteByte('>')
return nil
}
func ( *printer) ( Name) error {
if .Local == "" {
return fmt.Errorf("xml: end tag with no name")
}
if len(.tags) == 0 || .tags[len(.tags)-1].Local == "" {
return fmt.Errorf("xml: end tag </%s> without start tag", .Local)
}
if := .tags[len(.tags)-1]; != {
if .Local != .Local {
return fmt.Errorf("xml: end tag </%s> does not match start tag <%s>", .Local, .Local)
}
return fmt.Errorf("xml: end tag </%s> in namespace %s does not match start tag <%s> in namespace %s", .Local, .Space, .Local, .Space)
}
.tags = .tags[:len(.tags)-1]
.writeIndent(-1)
.WriteByte('<')
.WriteByte('/')
.WriteString(.Local)
.WriteByte('>')
.popPrefix()
return nil
}
func ( *printer) ( reflect.Type, reflect.Value) (string, []byte, error) {
switch .Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(.Int(), 10), nil, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return strconv.FormatUint(.Uint(), 10), nil, nil
case reflect.Float32, reflect.Float64:
return strconv.FormatFloat(.Float(), 'g', -1, .Type().Bits()), nil, nil
case reflect.String:
return .String(), nil, nil
case reflect.Bool:
return strconv.FormatBool(.Bool()), nil, nil
case reflect.Array:
if .Elem().Kind() != reflect.Uint8 {
break
}
var []byte
if .CanAddr() {
= .Slice(0, .Len()).Bytes()
} else {
= make([]byte, .Len())
reflect.Copy(reflect.ValueOf(), )
}
return "", , nil
case reflect.Slice:
if .Elem().Kind() != reflect.Uint8 {
break
}
return "", .Bytes(), nil
}
return "", nil, &UnsupportedTypeError{}
}
var ddBytes = []byte("--")
func ( reflect.Value) reflect.Value {
for .Kind() == reflect.Interface || .Kind() == reflect.Ptr {
if .IsNil() {
return
}
= .Elem()
}
return
}
func ( *printer) ( *typeInfo, reflect.Value) error {
:= parentStack{p: }
for := range .fields {
:= &.fields[]
if .flags&fAttr != 0 {
continue
}
:= .value(, dontInitNilPointers)
if !.IsValid() {
continue
}
switch .flags & fMode {
case fCDATA, fCharData:
:= EscapeText
if .flags&fMode == fCDATA {
= emitCDATA
}
if := .trim(.parents); != nil {
return
}
if .CanInterface() && .Type().Implements(textMarshalerType) {
, := .Interface().(encoding.TextMarshaler).MarshalText()
if != nil {
return
}
if := (, ); != nil {
return
}
continue
}
if .CanAddr() {
:= .Addr()
if .CanInterface() && .Type().Implements(textMarshalerType) {
, := .Interface().(encoding.TextMarshaler).MarshalText()
if != nil {
return
}
if := (, ); != nil {
return
}
continue
}
}
var [64]byte
= indirect()
switch .Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if := (, strconv.AppendInt([:0], .Int(), 10)); != nil {
return
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
if := (, strconv.AppendUint([:0], .Uint(), 10)); != nil {
return
}
case reflect.Float32, reflect.Float64:
if := (, strconv.AppendFloat([:0], .Float(), 'g', -1, .Type().Bits())); != nil {
return
}
case reflect.Bool:
if := (, strconv.AppendBool([:0], .Bool())); != nil {
return
}
case reflect.String:
if := (, []byte(.String())); != nil {
return
}
case reflect.Slice:
if , := .Interface().([]byte); {
if := (, ); != nil {
return
}
}
}
continue
case fComment:
if := .trim(.parents); != nil {
return
}
= indirect()
:= .Kind()
if !( == reflect.String || == reflect.Slice && .Type().Elem().Kind() == reflect.Uint8) {
return fmt.Errorf("xml: bad type for comment field of %s", .Type())
}
if .Len() == 0 {
continue
}
.writeIndent(0)
.WriteString("<!--")
:= false
:= false
switch {
case reflect.String:
:= .String()
= strings.Contains(, "--")
= [len()-1] == '-'
if ! {
.WriteString()
}
case reflect.Slice:
:= .Bytes()
= bytes.Contains(, ddBytes)
= [len()-1] == '-'
if ! {
.Write()
}
default:
panic("can't happen")
}
if {
return fmt.Errorf(`xml: comments must not contain "--"`)
}
if {
.WriteByte(' ')
}
.WriteString("-->")
continue
case fInnerXML:
= indirect()
:= .Interface()
switch raw := .(type) {
case []byte:
.Write()
continue
case string:
.WriteString()
continue
}
case fElement, fElement | fAny:
if := .trim(.parents); != nil {
return
}
if len(.parents) > len(.stack) {
if .Kind() != reflect.Ptr && .Kind() != reflect.Interface || !.IsNil() {
if := .push(.parents[len(.stack):]); != nil {
return
}
}
}
}
if := .marshalValue(, , nil); != nil {
return
}
}
.trim(nil)
return .cachedWriteError()
}
func ( *printer) () error {
, := .Write(nil)
return
}
func ( *printer) ( int) {
if len(.prefix) == 0 && len(.indent) == 0 {
return
}
if < 0 {
.depth--
if .indentedIn {
.indentedIn = false
return
}
.indentedIn = false
}
if .putNewline {
.WriteByte('\n')
} else {
.putNewline = true
}
if len(.prefix) > 0 {
.WriteString(.prefix)
}
if len(.indent) > 0 {
for := 0; < .depth; ++ {
.WriteString(.indent)
}
}
if > 0 {
.depth++
.indentedIn = true
}
}
type parentStack struct {
p *printer
stack []string
}
func ( *parentStack) ( []string) error {
:= 0
for ; < len() && < len(.stack); ++ {
if [] != .stack[] {
break
}
}
for := len(.stack) - 1; >= ; -- {
if := .p.writeEnd(Name{Local: .stack[]}); != nil {
return
}
}
.stack = .stack[:]
return nil
}
func ( *parentStack) ( []string) error {
for := 0; < len(); ++ {
if := .p.writeStart(&StartElement{Name: Name{Local: []}}); != nil {
return
}
}
.stack = append(.stack, ...)
return nil
}
type UnsupportedTypeError struct {
Type reflect.Type
}
func ( *UnsupportedTypeError) () string {
return "xml: unsupported type: " + .Type.String()
}
func ( reflect.Value) bool {
switch .Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return .Len() == 0
case reflect.Bool:
return !.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return .Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return .Uint() == 0
case reflect.Float32, reflect.Float64:
return .Float() == 0
case reflect.Interface, reflect.Ptr:
return .IsNil()
}
return false
}