// anyxml - marshal an XML document from almost any Go variable // Marshal XML from map[string]interface{}, arrays, slices, alpha/numeric, etc. // // Wraps xml.Marshal with functionality in github.com/clbanning/mxj to create // a more genericized XML marshaling capability. Note: unmarshaling the resultant // XML may not return the original value, since tag labels may have been injected // to create the XML representation of the value. // // See mxj package documentation for more information. See anyxml_test.go for // examples or just try Xml() or XmlIndent(). /* Encode an arbitrary JSON object. package main import ( "encoding/json" "fmt" "github.com/clbanning/anyxml" ) func main() { jsondata := []byte(`[ { "somekey":"somevalue" }, "string", 3.14159265, true ]`) var i interface{} err := json.Unmarshal(jsondata, &i) if err != nil { // do something } x, err := anyxml.XmlIndent(i, "", " ", "mydoc") if err != nil { // do something else } fmt.Println(string(x)) } output: somevalue string 3.14159265 true */ package anyxml import ( "encoding/xml" "reflect" "time" ) // Encode arbitrary value as XML. Note: there are no guarantees. func Xml(v interface{}, rootTag ...string) ([]byte, error) { if reflect.TypeOf(v).Kind() == reflect.Struct { return xml.Marshal(v) } var err error s := new(string) p := new(pretty) var rt string if len(rootTag) == 1 { rt = rootTag[0] } else { rt = DefaultRootTag } var ss string var b []byte switch v.(type) { case []interface{}: ss = "<" + rt + ">" for _, vv := range v.([]interface{}) { switch vv.(type) { case map[string]interface{}: m := vv.(map[string]interface{}) if len(m) == 1 { for tag, val := range m { err = mapToXmlIndent(false, s, tag, val, p) } } else { err = mapToXmlIndent(false, s, "element", vv, p) } default: err = mapToXmlIndent(false, s, "element", vv, p) } if err != nil { break } } ss += *s + "" b = []byte(ss) case map[string]interface{}: b, err = anyxml(v.(map[string]interface{}), rootTag...) case []map[string]interface{}: for _, vv := range v.([]map[string]interface{}) { b, err = anyxml(vv, rootTag...) ss += string(b) if err != nil { break } } b = []byte(ss) default: err = mapToXmlIndent(false, s, rt, v, p) b = []byte(*s) } return b, err } // Encode arbitrary value as XML. Note: there are no guarantees. func XmlWithDateFormat(dateFormat string, v interface{}, rootTag ...string) ([]byte, error) { if reflect.TypeOf(v).Kind() == reflect.Struct { return xml.Marshal(v) } var err error s := new(string) p := new(pretty) var rt string if len(rootTag) == 1 { rt = rootTag[0] } else { rt = DefaultRootTag } var ss string var b []byte switch v.(type) { case []interface{}: ss = "<" + rt + ">" for _, vv := range v.([]interface{}) { switch vv.(type) { case map[string]interface{}: m := vv.(map[string]interface{}) if len(m) == 1 { for tag, val := range m { err = mapToXmlIndentWithDateFormat(dateFormat, false, s, tag, val, p) } } else { err = mapToXmlIndentWithDateFormat(dateFormat, false, s, "element", vv, p) } default: err = mapToXmlIndentWithDateFormat(dateFormat, false, s, "element", vv, p) } if err != nil { break } } ss += *s + "" b = []byte(ss) case map[string]interface{}: b, err = anyxmlWithDateFormat(dateFormat, v.(map[string]interface{}), rootTag...) case []map[string]interface{}: for _, vv := range v.([]map[string]interface{}) { b, err = anyxmlWithDateFormat(dateFormat, vv, "element") ss += (string(b) + "\n") if err != nil { break } } b = []byte(ss) default: err = mapToXmlIndentWithDateFormat(dateFormat, false, s, rt, v, p) b = []byte(*s) } return b, err } // Encode an arbitrary value as a pretty XML string. Note: there are no guarantees. func XmlIndent(v interface{}, prefix, indent string, rootTag ...string) ([]byte, error) { if reflect.TypeOf(v).Kind() == reflect.Struct { return xml.MarshalIndent(v, prefix, indent) } var err error s := new(string) p := new(pretty) p.indent = indent p.padding = prefix var rt string if len(rootTag) == 1 { rt = rootTag[0] } else { rt = DefaultRootTag } var ss string var b []byte switch v.(type) { case []interface{}: ss = "<" + rt + ">\n" p.Indent() for _, vv := range v.([]interface{}) { switch vv.(type) { case map[string]interface{}: m := vv.(map[string]interface{}) if len(m) == 1 { for tag, val := range m { err = mapToXmlIndent(true, s, tag, val, p) } } else { p.start = 1 // we're 1 tag in to the doc err = mapToXmlIndent(true, s, "element", vv, p) *s += "\n" } case []map[string]interface{}: *s += p.padding + "\n" + p.padding for _, vvv := range vv.([]map[string]interface{}) { err = mapToXmlIndent(true, s, "element", vvv, p) *s += "\n" if err != nil { break } } *s += "\n" default: p.start = 0 err = mapToXmlIndent(true, s, "element", vv, p) } if err != nil { break } } ss += *s + "" b = []byte(ss) case map[string]interface{}: b, err = anyxmlIndent(v.(map[string]interface{}), prefix, indent, rootTag...) case []map[string]interface{}: for _, vv := range v.([]map[string]interface{}) { b, err = anyxmlIndent(vv, prefix, indent, rootTag...) ss += (string(b) + "\n") if err != nil { break } } b = []byte(ss) default: err = mapToXmlIndent(true, s, rt, v, p) b = []byte(*s) } return b, err } // Encode an arbitrary value as a pretty XML string. Note: there are no guarantees. func XmlIndentWithDateFormat(dateFormat string, v interface{}, prefix, indent string, rootTag ...string) ([]byte, error) { if reflect.TypeOf(v).Kind() == reflect.Struct { return xml.MarshalIndent(v, prefix, indent) } var err error s := new(string) p := new(pretty) p.indent = indent p.padding = prefix var rt string if len(rootTag) == 1 { rt = rootTag[0] } else { rt = DefaultRootTag } var ss string var b []byte switch v.(type) { case []interface{}: ss = "<" + rt + ">\n" p.Indent() for _, vv := range v.([]interface{}) { switch vv.(type) { case map[string]interface{}: m := vv.(map[string]interface{}) if len(m) == 1 { for tag, val := range m { err = mapToXmlIndentWithDateFormat(dateFormat, true, s, tag, val, p) } } else { p.start = 1 // we're 1 tag in to the doc err = mapToXmlIndentWithDateFormat(dateFormat, true, s, "element", vv, p) *s += "\n" } default: p.start = 0 err = mapToXmlIndentWithDateFormat(dateFormat, true, s, "element", vv, p) } if err != nil { break } } ss += *s + "" b = []byte(ss) case map[string]interface{}: b, err = anyxmlIndentWithDateFormat(dateFormat, v.(map[string]interface{}), prefix, indent, rootTag...) case []map[string]interface{}: for _, vv := range v.([]map[string]interface{}) { b, err = anyxmlIndentWithDateFormat(dateFormat, vv, prefix, indent, rootTag...) ss += (string(b) + "\n") if err != nil { break } } b = []byte(ss) default: err = mapToXmlIndentWithDateFormat(dateFormat, true, s, rt, v, p) b = []byte(*s) } return b, err } func Struct2MapWithDateFormat(dateFormat string, obj interface{}) map[string]interface{} { t := reflect.TypeOf(obj) v := reflect.ValueOf(obj) var data = make(map[string]interface{}) for i := 0; i < t.NumField(); i++ { if t.Field(i).Type == reflect.TypeOf(time.Now()) { data[t.Field(i).Name] = (v.Field(i).Interface().(time.Time)).Format(dateFormat) } else { data[t.Field(i).Name] = v.Field(i).Interface() } } return data }