anyxml.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. // anyxml - marshal an XML document from almost any Go variable
  2. // Marshal XML from map[string]interface{}, arrays, slices, alpha/numeric, etc.
  3. //
  4. // Wraps xml.Marshal with functionality in github.com/clbanning/mxj to create
  5. // a more genericized XML marshaling capability. Note: unmarshaling the resultant
  6. // XML may not return the original value, since tag labels may have been injected
  7. // to create the XML representation of the value.
  8. //
  9. // See mxj package documentation for more information. See anyxml_test.go for
  10. // examples or just try Xml() or XmlIndent().
  11. /*
  12. Encode an arbitrary JSON object.
  13. package main
  14. import (
  15. "encoding/json"
  16. "fmt"
  17. "github.com/clbanning/anyxml"
  18. )
  19. func main() {
  20. jsondata := []byte(`[
  21. { "somekey":"somevalue" },
  22. "string",
  23. 3.14159265,
  24. true
  25. ]`)
  26. var i interface{}
  27. err := json.Unmarshal(jsondata, &i)
  28. if err != nil {
  29. // do something
  30. }
  31. x, err := anyxml.XmlIndent(i, "", " ", "mydoc")
  32. if err != nil {
  33. // do something else
  34. }
  35. fmt.Println(string(x))
  36. }
  37. output:
  38. <mydoc>
  39. <somekey>somevalue</somekey>
  40. <element>string</element>
  41. <element>3.14159265</element>
  42. <element>true</element>
  43. </mydoc>
  44. */
  45. package anyxml
  46. import (
  47. "encoding/xml"
  48. "reflect"
  49. "time"
  50. )
  51. // Encode arbitrary value as XML. Note: there are no guarantees.
  52. func Xml(v interface{}, rootTag ...string) ([]byte, error) {
  53. if reflect.TypeOf(v).Kind() == reflect.Struct {
  54. return xml.Marshal(v)
  55. }
  56. var err error
  57. s := new(string)
  58. p := new(pretty)
  59. var rt string
  60. if len(rootTag) == 1 {
  61. rt = rootTag[0]
  62. } else {
  63. rt = DefaultRootTag
  64. }
  65. var ss string
  66. var b []byte
  67. switch v.(type) {
  68. case []interface{}:
  69. ss = "<" + rt + ">"
  70. for _, vv := range v.([]interface{}) {
  71. switch vv.(type) {
  72. case map[string]interface{}:
  73. m := vv.(map[string]interface{})
  74. if len(m) == 1 {
  75. for tag, val := range m {
  76. err = mapToXmlIndent(false, s, tag, val, p)
  77. }
  78. } else {
  79. err = mapToXmlIndent(false, s, "element", vv, p)
  80. }
  81. default:
  82. err = mapToXmlIndent(false, s, "element", vv, p)
  83. }
  84. if err != nil {
  85. break
  86. }
  87. }
  88. ss += *s + "</" + rt + ">"
  89. b = []byte(ss)
  90. case map[string]interface{}:
  91. b, err = anyxml(v.(map[string]interface{}), rootTag...)
  92. case []map[string]interface{}:
  93. for _, vv := range v.([]map[string]interface{}) {
  94. b, err = anyxml(vv, rootTag...)
  95. ss += string(b)
  96. if err != nil {
  97. break
  98. }
  99. }
  100. b = []byte(ss)
  101. default:
  102. err = mapToXmlIndent(false, s, rt, v, p)
  103. b = []byte(*s)
  104. }
  105. return b, err
  106. }
  107. // Encode arbitrary value as XML. Note: there are no guarantees.
  108. func XmlWithDateFormat(dateFormat string, v interface{}, rootTag ...string) ([]byte, error) {
  109. if reflect.TypeOf(v).Kind() == reflect.Struct {
  110. return xml.Marshal(v)
  111. }
  112. var err error
  113. s := new(string)
  114. p := new(pretty)
  115. var rt string
  116. if len(rootTag) == 1 {
  117. rt = rootTag[0]
  118. } else {
  119. rt = DefaultRootTag
  120. }
  121. var ss string
  122. var b []byte
  123. switch v.(type) {
  124. case []interface{}:
  125. ss = "<" + rt + ">"
  126. for _, vv := range v.([]interface{}) {
  127. switch vv.(type) {
  128. case map[string]interface{}:
  129. m := vv.(map[string]interface{})
  130. if len(m) == 1 {
  131. for tag, val := range m {
  132. err = mapToXmlIndentWithDateFormat(dateFormat, false, s, tag, val, p)
  133. }
  134. } else {
  135. err = mapToXmlIndentWithDateFormat(dateFormat, false, s, "element", vv, p)
  136. }
  137. default:
  138. err = mapToXmlIndentWithDateFormat(dateFormat, false, s, "element", vv, p)
  139. }
  140. if err != nil {
  141. break
  142. }
  143. }
  144. ss += *s + "</" + rt + ">"
  145. b = []byte(ss)
  146. case map[string]interface{}:
  147. b, err = anyxmlWithDateFormat(dateFormat, v.(map[string]interface{}), rootTag...)
  148. case []map[string]interface{}:
  149. for _, vv := range v.([]map[string]interface{}) {
  150. b, err = anyxmlWithDateFormat(dateFormat, vv, "element")
  151. ss += (string(b) + "\n")
  152. if err != nil {
  153. break
  154. }
  155. }
  156. b = []byte(ss)
  157. default:
  158. err = mapToXmlIndentWithDateFormat(dateFormat, false, s, rt, v, p)
  159. b = []byte(*s)
  160. }
  161. return b, err
  162. }
  163. // Encode an arbitrary value as a pretty XML string. Note: there are no guarantees.
  164. func XmlIndent(v interface{}, prefix, indent string, rootTag ...string) ([]byte, error) {
  165. if reflect.TypeOf(v).Kind() == reflect.Struct {
  166. return xml.MarshalIndent(v, prefix, indent)
  167. }
  168. var err error
  169. s := new(string)
  170. p := new(pretty)
  171. p.indent = indent
  172. p.padding = prefix
  173. var rt string
  174. if len(rootTag) == 1 {
  175. rt = rootTag[0]
  176. } else {
  177. rt = DefaultRootTag
  178. }
  179. var ss string
  180. var b []byte
  181. switch v.(type) {
  182. case []interface{}:
  183. ss = "<" + rt + ">\n"
  184. p.Indent()
  185. for _, vv := range v.([]interface{}) {
  186. switch vv.(type) {
  187. case map[string]interface{}:
  188. m := vv.(map[string]interface{})
  189. if len(m) == 1 {
  190. for tag, val := range m {
  191. err = mapToXmlIndent(true, s, tag, val, p)
  192. }
  193. } else {
  194. p.start = 1 // we're 1 tag in to the doc
  195. err = mapToXmlIndent(true, s, "element", vv, p)
  196. *s += "\n"
  197. }
  198. case []map[string]interface{}:
  199. *s += p.padding + "<element>\n" + p.padding
  200. for _, vvv := range vv.([]map[string]interface{}) {
  201. err = mapToXmlIndent(true, s, "element", vvv, p)
  202. *s += "\n"
  203. if err != nil {
  204. break
  205. }
  206. }
  207. *s += "</element>\n"
  208. default:
  209. p.start = 0
  210. err = mapToXmlIndent(true, s, "element", vv, p)
  211. }
  212. if err != nil {
  213. break
  214. }
  215. }
  216. ss += *s + "</" + rt + ">"
  217. b = []byte(ss)
  218. case map[string]interface{}:
  219. b, err = anyxmlIndent(v.(map[string]interface{}), prefix, indent, rootTag...)
  220. case []map[string]interface{}:
  221. for _, vv := range v.([]map[string]interface{}) {
  222. b, err = anyxmlIndent(vv, prefix, indent, rootTag...)
  223. ss += (string(b) + "\n")
  224. if err != nil {
  225. break
  226. }
  227. }
  228. b = []byte(ss)
  229. default:
  230. err = mapToXmlIndent(true, s, rt, v, p)
  231. b = []byte(*s)
  232. }
  233. return b, err
  234. }
  235. // Encode an arbitrary value as a pretty XML string. Note: there are no guarantees.
  236. func XmlIndentWithDateFormat(dateFormat string, v interface{}, prefix, indent string, rootTag ...string) ([]byte, error) {
  237. if reflect.TypeOf(v).Kind() == reflect.Struct {
  238. return xml.MarshalIndent(v, prefix, indent)
  239. }
  240. var err error
  241. s := new(string)
  242. p := new(pretty)
  243. p.indent = indent
  244. p.padding = prefix
  245. var rt string
  246. if len(rootTag) == 1 {
  247. rt = rootTag[0]
  248. } else {
  249. rt = DefaultRootTag
  250. }
  251. var ss string
  252. var b []byte
  253. switch v.(type) {
  254. case []interface{}:
  255. ss = "<" + rt + ">\n"
  256. p.Indent()
  257. for _, vv := range v.([]interface{}) {
  258. switch vv.(type) {
  259. case map[string]interface{}:
  260. m := vv.(map[string]interface{})
  261. if len(m) == 1 {
  262. for tag, val := range m {
  263. err = mapToXmlIndentWithDateFormat(dateFormat, true, s, tag, val, p)
  264. }
  265. } else {
  266. p.start = 1 // we're 1 tag in to the doc
  267. err = mapToXmlIndentWithDateFormat(dateFormat, true, s, "element", vv, p)
  268. *s += "\n"
  269. }
  270. default:
  271. p.start = 0
  272. err = mapToXmlIndentWithDateFormat(dateFormat, true, s, "element", vv, p)
  273. }
  274. if err != nil {
  275. break
  276. }
  277. }
  278. ss += *s + "</" + rt + ">"
  279. b = []byte(ss)
  280. case map[string]interface{}:
  281. b, err = anyxmlIndentWithDateFormat(dateFormat, v.(map[string]interface{}), prefix, indent, rootTag...)
  282. case []map[string]interface{}:
  283. for _, vv := range v.([]map[string]interface{}) {
  284. b, err = anyxmlIndentWithDateFormat(dateFormat, vv, prefix, indent, rootTag...)
  285. ss += (string(b) + "\n")
  286. if err != nil {
  287. break
  288. }
  289. }
  290. b = []byte(ss)
  291. default:
  292. err = mapToXmlIndentWithDateFormat(dateFormat, true, s, rt, v, p)
  293. b = []byte(*s)
  294. }
  295. return b, err
  296. }
  297. func Struct2MapWithDateFormat(dateFormat string, obj interface{}) map[string]interface{} {
  298. t := reflect.TypeOf(obj)
  299. v := reflect.ValueOf(obj)
  300. var data = make(map[string]interface{})
  301. for i := 0; i < t.NumField(); i++ {
  302. if t.Field(i).Type == reflect.TypeOf(time.Now()) {
  303. data[t.Field(i).Name] = (v.Field(i).Interface().(time.Time)).Format(dateFormat)
  304. } else {
  305. data[t.Field(i).Name] = v.Field(i).Interface()
  306. }
  307. }
  308. return data
  309. }