package xml
import (
"bufio"
"bytes"
"encoding"
"errors"
"fmt"
"io"
"reflect"
"strconv"
"strings"
)
const (
Header = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
)
func Marshal (v any ) ([]byte , error ) {
var b bytes .Buffer
enc := NewEncoder (&b )
if err := enc .Encode (v ); err != nil {
return nil , err
}
if err := enc .Close (); err != nil {
return nil , err
}
return b .Bytes (), nil
}
type Marshaler interface {
MarshalXML (e *Encoder , start StartElement ) error
}
type MarshalerAttr interface {
MarshalXMLAttr (name Name ) (Attr , error )
}
func MarshalIndent (v any , prefix , indent string ) ([]byte , error ) {
var b bytes .Buffer
enc := NewEncoder (&b )
enc .Indent (prefix , indent )
if err := enc .Encode (v ); err != nil {
return nil , err
}
if err := enc .Close (); err != nil {
return nil , err
}
return b .Bytes (), nil
}
type Encoder struct {
p printer
}
func NewEncoder (w io .Writer ) *Encoder {
e := &Encoder {printer {w : bufio .NewWriter (w )}}
e .p .encoder = e
return e
}
func (enc *Encoder ) Indent (prefix , indent string ) {
enc .p .prefix = prefix
enc .p .indent = indent
}
func (enc *Encoder ) Encode (v any ) error {
err := enc .p .marshalValue (reflect .ValueOf (v ), nil , nil )
if err != nil {
return err
}
return enc .p .w .Flush ()
}
func (enc *Encoder ) EncodeElement (v any , start StartElement ) error {
err := enc .p .marshalValue (reflect .ValueOf (v ), nil , &start )
if err != nil {
return err
}
return enc .p .w .Flush ()
}
var (
begComment = []byte ("<!--" )
endComment = []byte ("-->" )
endProcInst = []byte ("?>" )
)
func (enc *Encoder ) EncodeToken (t Token ) error {
p := &enc .p
switch t := t .(type ) {
case StartElement :
if err := p .writeStart (&t ); err != nil {
return err
}
case EndElement :
if err := p .writeEnd (t .Name ); err != nil {
return err
}
case CharData :
escapeText (p , t , false )
case Comment :
if bytes .Contains (t , endComment ) {
return fmt .Errorf ("xml: EncodeToken of Comment containing --> marker" )
}
p .WriteString ("<!--" )
p .Write (t )
p .WriteString ("-->" )
return p .cachedWriteError ()
case ProcInst :
if t .Target == "xml" && p .w .Buffered () != 0 {
return fmt .Errorf ("xml: EncodeToken of ProcInst xml target only valid for xml declaration, first token encoded" )
}
if !isNameString (t .Target ) {
return fmt .Errorf ("xml: EncodeToken of ProcInst with invalid Target" )
}
if bytes .Contains (t .Inst , endProcInst ) {
return fmt .Errorf ("xml: EncodeToken of ProcInst containing ?> marker" )
}
p .WriteString ("<?" )
p .WriteString (t .Target )
if len (t .Inst ) > 0 {
p .WriteByte (' ' )
p .Write (t .Inst )
}
p .WriteString ("?>" )
case Directive :
if !isValidDirective (t ) {
return fmt .Errorf ("xml: EncodeToken of Directive containing wrong < or > markers" )
}
p .WriteString ("<!" )
p .Write (t )
p .WriteString (">" )
default :
return fmt .Errorf ("xml: EncodeToken of invalid token type" )
}
return p .cachedWriteError ()
}
func isValidDirective(dir Directive ) bool {
var (
depth int
inquote uint8
incomment bool
)
for i , c := range dir {
switch {
case incomment :
if c == '>' {
if n := 1 + i - len (endComment ); n >= 0 && bytes .Equal (dir [n :i +1 ], endComment ) {
incomment = false
}
}
case inquote != 0 :
if c == inquote {
inquote = 0
}
case c == '\'' || c == '"' :
inquote = c
case c == '<' :
if i +len (begComment ) < len (dir ) && bytes .Equal (dir [i :i +len (begComment )], begComment ) {
incomment = true
} else {
depth ++
}
case c == '>' :
if depth == 0 {
return false
}
depth --
}
}
return depth == 0 && inquote == 0 && !incomment
}
func (enc *Encoder ) Flush () error {
return enc .p .w .Flush ()
}
func (enc *Encoder ) Close () error {
return enc .p .Close ()
}
type printer struct {
w *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
closed bool
err error
}
func (p *printer ) createAttrPrefix (url string ) string {
if prefix := p .attrPrefix [url ]; prefix != "" {
return prefix
}
if url == xmlURL {
return xmlPrefix
}
if p .attrPrefix == nil {
p .attrPrefix = make (map [string ]string )
p .attrNS = make (map [string ]string )
}
prefix := strings .TrimRight (url , "/" )
if i := strings .LastIndex (prefix , "/" ); i >= 0 {
prefix = prefix [i +1 :]
}
if prefix == "" || !isName ([]byte (prefix )) || strings .Contains (prefix , ":" ) {
prefix = "_"
}
if len (prefix ) >= 3 && strings .EqualFold (prefix [:3 ], "xml" ) {
prefix = "_" + prefix
}
if p .attrNS [prefix ] != "" {
for p .seq ++; ; p .seq ++ {
if id := prefix + "_" + strconv .Itoa (p .seq ); p .attrNS [id ] == "" {
prefix = id
break
}
}
}
p .attrPrefix [url ] = prefix
p .attrNS [prefix ] = url
p .WriteString (`xmlns:` )
p .WriteString (prefix )
p .WriteString (`="` )
EscapeText (p , []byte (url ))
p .WriteString (`" ` )
p .prefixes = append (p .prefixes , prefix )
return prefix
}
func (p *printer ) deleteAttrPrefix (prefix string ) {
delete (p .attrPrefix , p .attrNS [prefix ])
delete (p .attrNS , prefix )
}
func (p *printer ) markPrefix () {
p .prefixes = append (p .prefixes , "" )
}
func (p *printer ) popPrefix () {
for len (p .prefixes ) > 0 {
prefix := p .prefixes [len (p .prefixes )-1 ]
p .prefixes = p .prefixes [:len (p .prefixes )-1 ]
if prefix == "" {
break
}
p .deleteAttrPrefix (prefix )
}
}
var (
marshalerType = reflect .TypeFor [Marshaler ]()
marshalerAttrType = reflect .TypeFor [MarshalerAttr ]()
textMarshalerType = reflect .TypeFor [encoding .TextMarshaler ]()
)
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
}
for val .Kind () == reflect .Interface || val .Kind () == reflect .Pointer {
if val .IsNil () {
return nil
}
val = val .Elem ()
}
kind := val .Kind ()
typ := val .Type ()
if val .CanInterface () && typ .Implements (marshalerType ) {
return p .marshalInterface (val .Interface ().(Marshaler ), defaultStart (typ , finfo , startTemplate ))
}
if val .CanAddr () {
pv := val .Addr ()
if pv .CanInterface () && pv .Type ().Implements (marshalerType ) {
return p .marshalInterface (pv .Interface ().(Marshaler ), defaultStart (pv .Type (), finfo , startTemplate ))
}
}
if val .CanInterface () && typ .Implements (textMarshalerType ) {
return p .marshalTextInterface (val .Interface ().(encoding .TextMarshaler ), defaultStart (typ , finfo , startTemplate ))
}
if val .CanAddr () {
pv := val .Addr ()
if pv .CanInterface () && pv .Type ().Implements (textMarshalerType ) {
return p .marshalTextInterface (pv .Interface ().(encoding .TextMarshaler ), defaultStart (pv .Type (), finfo , startTemplate ))
}
}
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
}
var start StartElement
if startTemplate != nil {
start .Name = startTemplate .Name
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 {
fv := xmlname .value (val , dontInitNilPointers )
if v , ok := fv .Interface ().(Name ); ok && v .Local != "" {
start .Name = v
}
}
}
if start .Name .Local == "" && finfo != nil {
start .Name .Space , start .Name .Local = finfo .xmlns , finfo .name
}
if start .Name .Local == "" {
name := typ .Name ()
if i := strings .IndexByte (name , '[' ); i >= 0 {
name = name [:i ]
}
if name == "" {
return &UnsupportedTypeError {typ }
}
start .Name .Local = name
}
for i := range tinfo .fields {
finfo := &tinfo .fields [i ]
if finfo .flags &fAttr == 0 {
continue
}
fv := finfo .value (val , dontInitNilPointers )
if finfo .flags &fOmitEmpty != 0 && (!fv .IsValid () || isEmptyValue (fv )) {
continue
}
if fv .Kind () == reflect .Interface && fv .IsNil () {
continue
}
name := Name {Space : finfo .xmlns , Local : finfo .name }
if err := p .marshalAttr (&start , name , fv ); err != nil {
return err
}
}
if tinfo .xmlname != nil && start .Name .Space == "" &&
tinfo .xmlname .xmlns == "" && tinfo .xmlname .name == "" &&
len (p .tags ) != 0 && p .tags [len (p .tags )-1 ].Space != "" {
start .Attr = append (start .Attr , Attr {Name {"" , xmlnsPrefix }, "" })
}
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 ()
}
func (p *printer ) marshalAttr (start *StartElement , name Name , val reflect .Value ) error {
if val .CanInterface () && val .Type ().Implements (marshalerAttrType ) {
attr , err := val .Interface ().(MarshalerAttr ).MarshalXMLAttr (name )
if err != nil {
return err
}
if attr .Name .Local != "" {
start .Attr = append (start .Attr , attr )
}
return nil
}
if val .CanAddr () {
pv := val .Addr ()
if pv .CanInterface () && pv .Type ().Implements (marshalerAttrType ) {
attr , err := pv .Interface ().(MarshalerAttr ).MarshalXMLAttr (name )
if err != nil {
return err
}
if attr .Name .Local != "" {
start .Attr = append (start .Attr , attr )
}
return nil
}
}
if val .CanInterface () && val .Type ().Implements (textMarshalerType ) {
text , err := val .Interface ().(encoding .TextMarshaler ).MarshalText ()
if err != nil {
return err
}
start .Attr = append (start .Attr , Attr {name , string (text )})
return nil
}
if val .CanAddr () {
pv := val .Addr ()
if pv .CanInterface () && pv .Type ().Implements (textMarshalerType ) {
text , err := pv .Interface ().(encoding .TextMarshaler ).MarshalText ()
if err != nil {
return err
}
start .Attr = append (start .Attr , Attr {name , string (text )})
return nil
}
}
switch val .Kind () {
case reflect .Pointer , reflect .Interface :
if val .IsNil () {
return nil
}
val = val .Elem ()
}
if val .Kind () == reflect .Slice && val .Type ().Elem ().Kind () != reflect .Uint8 {
n := val .Len ()
for i := 0 ; i < n ; i ++ {
if err := p .marshalAttr (start , name , val .Index (i )); err != nil {
return err
}
}
return nil
}
if val .Type () == attrType {
start .Attr = append (start .Attr , val .Interface ().(Attr ))
return nil
}
s , b , err := p .marshalSimple (val .Type (), val )
if err != nil {
return err
}
if b != nil {
s = string (b )
}
start .Attr = append (start .Attr , Attr {name , s })
return nil
}
func defaultStart(typ reflect .Type , finfo *fieldInfo , startTemplate *StartElement ) StartElement {
var start StartElement
if startTemplate != nil {
start .Name = startTemplate .Name
start .Attr = append (start .Attr , startTemplate .Attr ...)
} else if finfo != nil && finfo .name != "" {
start .Name .Local = finfo .name
start .Name .Space = finfo .xmlns
} else if typ .Name () != "" {
start .Name .Local = typ .Name ()
} else {
start .Name .Local = typ .Elem ().Name ()
}
return start
}
func (p *printer ) marshalInterface (val Marshaler , start StartElement ) error {
p .tags = append (p .tags , Name {})
n := len (p .tags )
err := val .MarshalXML (p .encoder , start )
if err != nil {
return err
}
if len (p .tags ) > n {
return fmt .Errorf ("xml: %s.MarshalXML wrote invalid XML: <%s> not closed" , receiverType (val ), p .tags [len (p .tags )-1 ].Local )
}
p .tags = p .tags [:n -1 ]
return nil
}
func (p *printer ) marshalTextInterface (val encoding .TextMarshaler , start StartElement ) error {
if err := p .writeStart (&start ); err != nil {
return err
}
text , err := val .MarshalText ()
if err != nil {
return err
}
EscapeText (p , text )
return p .writeEnd (start .Name )
}
func (p *printer ) writeStart (start *StartElement ) error {
if start .Name .Local == "" {
return fmt .Errorf ("xml: start tag with no name" )
}
p .tags = append (p .tags , start .Name )
p .markPrefix ()
p .writeIndent (1 )
p .WriteByte ('<' )
p .WriteString (start .Name .Local )
if start .Name .Space != "" {
p .WriteString (` xmlns="` )
p .EscapeString (start .Name .Space )
p .WriteByte ('"' )
}
for _ , attr := range start .Attr {
name := attr .Name
if name .Local == "" {
continue
}
p .WriteByte (' ' )
if name .Space != "" {
p .WriteString (p .createAttrPrefix (name .Space ))
p .WriteByte (':' )
}
p .WriteString (name .Local )
p .WriteString (`="` )
p .EscapeString (attr .Value )
p .WriteByte ('"' )
}
p .WriteByte ('>' )
return nil
}
func (p *printer ) writeEnd (name Name ) error {
if name .Local == "" {
return fmt .Errorf ("xml: end tag with no name" )
}
if len (p .tags ) == 0 || p .tags [len (p .tags )-1 ].Local == "" {
return fmt .Errorf ("xml: end tag </%s> without start tag" , name .Local )
}
if top := p .tags [len (p .tags )-1 ]; top != name {
if top .Local != name .Local {
return fmt .Errorf ("xml: end tag </%s> does not match start tag <%s>" , name .Local , top .Local )
}
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 )
}
p .tags = p .tags [:len (p .tags )-1 ]
p .writeIndent (-1 )
p .WriteByte ('<' )
p .WriteByte ('/' )
p .WriteString (name .Local )
p .WriteByte ('>' )
p .popPrefix ()
return nil
}
func (p *printer ) marshalSimple (typ reflect .Type , val reflect .Value ) (string , []byte , error ) {
switch val .Kind () {
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
return strconv .FormatInt (val .Int (), 10 ), nil , nil
case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
return strconv .FormatUint (val .Uint (), 10 ), nil , nil
case reflect .Float32 , reflect .Float64 :
return strconv .FormatFloat (val .Float (), 'g' , -1 , val .Type ().Bits ()), nil , nil
case reflect .String :
return val .String (), nil , nil
case reflect .Bool :
return strconv .FormatBool (val .Bool ()), nil , nil
case reflect .Array :
if typ .Elem ().Kind () != reflect .Uint8 {
break
}
var bytes []byte
if val .CanAddr () {
bytes = val .Bytes ()
} else {
bytes = make ([]byte , val .Len ())
reflect .Copy (reflect .ValueOf (bytes ), val )
}
return "" , bytes , nil
case reflect .Slice :
if typ .Elem ().Kind () != reflect .Uint8 {
break
}
return "" , val .Bytes (), nil
}
return "" , nil , &UnsupportedTypeError {typ }
}
var ddBytes = []byte ("--" )
func indirect(vf reflect .Value ) reflect .Value {
for vf .Kind () == reflect .Interface || vf .Kind () == reflect .Pointer {
if vf .IsNil () {
return vf
}
vf = vf .Elem ()
}
return vf
}
func (p *printer ) marshalStruct (tinfo *typeInfo , val reflect .Value ) error {
s := parentStack {p : p }
for i := range tinfo .fields {
finfo := &tinfo .fields [i ]
if finfo .flags &fAttr != 0 {
continue
}
vf := finfo .value (val , dontInitNilPointers )
if !vf .IsValid () {
continue
}
switch finfo .flags & fMode {
case fCDATA , fCharData :
emit := EscapeText
if finfo .flags &fMode == fCDATA {
emit = emitCDATA
}
if err := s .trim (finfo .parents ); err != nil {
return err
}
if vf .CanInterface () && vf .Type ().Implements (textMarshalerType ) {
data , err := vf .Interface ().(encoding .TextMarshaler ).MarshalText ()
if err != nil {
return err
}
if err := emit (p , data ); err != nil {
return err
}
continue
}
if vf .CanAddr () {
pv := vf .Addr ()
if pv .CanInterface () && pv .Type ().Implements (textMarshalerType ) {
data , err := pv .Interface ().(encoding .TextMarshaler ).MarshalText ()
if err != nil {
return err
}
if err := emit (p , data ); err != nil {
return err
}
continue
}
}
var scratch [64 ]byte
vf = indirect (vf )
switch vf .Kind () {
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
if err := emit (p , strconv .AppendInt (scratch [:0 ], vf .Int (), 10 )); err != nil {
return err
}
case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
if err := emit (p , strconv .AppendUint (scratch [:0 ], vf .Uint (), 10 )); err != nil {
return err
}
case reflect .Float32 , reflect .Float64 :
if err := emit (p , strconv .AppendFloat (scratch [:0 ], vf .Float (), 'g' , -1 , vf .Type ().Bits ())); err != nil {
return err
}
case reflect .Bool :
if err := emit (p , strconv .AppendBool (scratch [:0 ], vf .Bool ())); err != nil {
return err
}
case reflect .String :
if err := emit (p , []byte (vf .String ())); err != nil {
return err
}
case reflect .Slice :
if elem , ok := vf .Interface ().([]byte ); ok {
if err := emit (p , elem ); err != nil {
return err
}
}
}
continue
case fComment :
if err := s .trim (finfo .parents ); err != nil {
return err
}
vf = indirect (vf )
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 .writeIndent (0 )
p .WriteString ("<!--" )
dashDash := false
dashLast := false
switch k {
case reflect .String :
s := vf .String ()
dashDash = strings .Contains (s , "--" )
dashLast = s [len (s )-1 ] == '-'
if !dashDash {
p .WriteString (s )
}
case reflect .Slice :
b := vf .Bytes ()
dashDash = bytes .Contains (b , ddBytes )
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 {
p .WriteByte (' ' )
}
p .WriteString ("-->" )
continue
case fInnerXML :
vf = indirect (vf )
iface := vf .Interface ()
switch raw := iface .(type ) {
case []byte :
p .Write (raw )
continue
case string :
p .WriteString (raw )
continue
}
case fElement , fElement | fAny :
if err := s .trim (finfo .parents ); err != nil {
return err
}
if len (finfo .parents ) > len (s .stack ) {
if vf .Kind () != reflect .Pointer && vf .Kind () != reflect .Interface || !vf .IsNil () {
if err := s .push (finfo .parents [len (s .stack ):]); err != nil {
return err
}
}
}
}
if err := p .marshalValue (vf , finfo , nil ); err != nil {
return err
}
}
s .trim (nil )
return p .cachedWriteError ()
}
func (p *printer ) Write (b []byte ) (n int , err error ) {
if p .closed && p .err == nil {
p .err = errors .New ("use of closed Encoder" )
}
if p .err == nil {
n , p .err = p .w .Write (b )
}
return n , p .err
}
func (p *printer ) WriteString (s string ) (n int , err error ) {
if p .closed && p .err == nil {
p .err = errors .New ("use of closed Encoder" )
}
if p .err == nil {
n , p .err = p .w .WriteString (s )
}
return n , p .err
}
func (p *printer ) WriteByte (c byte ) error {
if p .closed && p .err == nil {
p .err = errors .New ("use of closed Encoder" )
}
if p .err == nil {
p .err = p .w .WriteByte (c )
}
return p .err
}
func (p *printer ) Close () error {
if p .closed {
return nil
}
p .closed = true
if err := p .w .Flush (); err != nil {
return err
}
if len (p .tags ) > 0 {
return fmt .Errorf ("unclosed tag <%s>" , p .tags [len (p .tags )-1 ].Local )
}
return nil
}
func (p *printer ) cachedWriteError () error {
_ , err := p .Write (nil )
return err
}
func (p *printer ) writeIndent (depthDelta int ) {
if len (p .prefix ) == 0 && len (p .indent ) == 0 {
return
}
if depthDelta < 0 {
p .depth --
if p .indentedIn {
p .indentedIn = false
return
}
p .indentedIn = false
}
if p .putNewline {
p .WriteByte ('\n' )
} else {
p .putNewline = true
}
if len (p .prefix ) > 0 {
p .WriteString (p .prefix )
}
if len (p .indent ) > 0 {
for i := 0 ; i < p .depth ; i ++ {
p .WriteString (p .indent )
}
}
if depthDelta > 0 {
p .depth ++
p .indentedIn = true
}
}
type parentStack struct {
p *printer
stack []string
}
func (s *parentStack ) trim (parents []string ) error {
split := 0
for ; split < len (parents ) && split < len (s .stack ); split ++ {
if parents [split ] != s .stack [split ] {
break
}
}
for i := len (s .stack ) - 1 ; i >= split ; i -- {
if err := s .p .writeEnd (Name {Local : s .stack [i ]}); err != nil {
return err
}
}
s .stack = s .stack [:split ]
return nil
}
func (s *parentStack ) push (parents []string ) error {
for i := 0 ; i < len (parents ); i ++ {
if err := s .p .writeStart (&StartElement {Name : Name {Local : parents [i ]}}); err != nil {
return err
}
}
s .stack = append (s .stack , parents ...)
return nil
}
type UnsupportedTypeError struct {
Type reflect .Type
}
func (e *UnsupportedTypeError ) Error () string {
return "xml: unsupported type: " + e .Type .String ()
}
func isEmptyValue(v reflect .Value ) bool {
switch v .Kind () {
case reflect .Array , reflect .Map , reflect .Slice , reflect .String :
return v .Len () == 0
case reflect .Bool ,
reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 ,
reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr ,
reflect .Float32 , reflect .Float64 ,
reflect .Interface , reflect .Pointer :
return v .IsZero ()
}
return false
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .