1package mxj
2
3import (
4	"bytes"
5	"encoding/xml"
6	"reflect"
7)
8
9const (
10	DefaultElementTag = "element"
11)
12
13// Encode arbitrary value as XML.
14//
15// Note: unmarshaling the resultant
16// XML may not return the original value, since tag labels may have been injected
17// to create the XML representation of the value.
18/*
19 Encode an arbitrary JSON object.
20	package main
21
22	import (
23		"encoding/json"
24		"fmt"
25		"github.com/clbanning/mxj"
26	)
27
28	func main() {
29		jsondata := []byte(`[
30			{ "somekey":"somevalue" },
31			"string",
32			3.14159265,
33			true
34		]`)
35		var i interface{}
36		err := json.Unmarshal(jsondata, &i)
37		if err != nil {
38			// do something
39		}
40		x, err := mxj.AnyXmlIndent(i, "", "  ", "mydoc")
41		if err != nil {
42			// do something else
43		}
44		fmt.Println(string(x))
45	}
46
47	output:
48		<mydoc>
49		  <somekey>somevalue</somekey>
50		  <element>string</element>
51		  <element>3.14159265</element>
52		  <element>true</element>
53		</mydoc>
54
55An extreme example is available in examples/goofy_map.go.
56*/
57// Alternative values for DefaultRootTag and DefaultElementTag can be set as:
58// AnyXml( v, myRootTag, myElementTag).
59func AnyXml(v interface{}, tags ...string) ([]byte, error) {
60	var rt, et string
61	if len(tags) == 1 || len(tags) == 2 {
62		rt = tags[0]
63	} else {
64		rt = DefaultRootTag
65	}
66	if len(tags) == 2 {
67		et = tags[1]
68	} else {
69		et = DefaultElementTag
70	}
71
72	if v == nil {
73		if useGoXmlEmptyElemSyntax {
74			return []byte("<" + rt + "></" + rt + ">"), nil
75		}
76		return []byte("<" + rt + "/>"), nil
77	}
78	if reflect.TypeOf(v).Kind() == reflect.Struct {
79		return xml.Marshal(v)
80	}
81
82	var err error
83	s := new(bytes.Buffer)
84	p := new(pretty)
85
86	var b []byte
87	switch v.(type) {
88	case []interface{}:
89		if _, err = s.WriteString("<" + rt + ">"); err != nil {
90			return nil, err
91		}
92		for _, vv := range v.([]interface{}) {
93			switch vv.(type) {
94			case map[string]interface{}:
95				m := vv.(map[string]interface{})
96				if len(m) == 1 {
97					for tag, val := range m {
98						err = marshalMapToXmlIndent(false, s, tag, val, p)
99					}
100				} else {
101					err = marshalMapToXmlIndent(false, s, et, vv, p)
102				}
103			default:
104				err = marshalMapToXmlIndent(false, s, et, vv, p)
105			}
106			if err != nil {
107				break
108			}
109		}
110		if _, err = s.WriteString("</" + rt + ">"); err != nil {
111			return nil, err
112		}
113		b = s.Bytes()
114	case map[string]interface{}:
115		m := Map(v.(map[string]interface{}))
116		b, err = m.Xml(rt)
117	default:
118		err = marshalMapToXmlIndent(false, s, rt, v, p)
119		b = s.Bytes()
120	}
121
122	return b, err
123}
124
125// Encode an arbitrary value as a pretty XML string.
126// Alternative values for DefaultRootTag and DefaultElementTag can be set as:
127// AnyXmlIndent( v, "", "  ", myRootTag, myElementTag).
128func AnyXmlIndent(v interface{}, prefix, indent string, tags ...string) ([]byte, error) {
129	var rt, et string
130	if len(tags) == 1 || len(tags) == 2 {
131		rt = tags[0]
132	} else {
133		rt = DefaultRootTag
134	}
135	if len(tags) == 2 {
136		et = tags[1]
137	} else {
138		et = DefaultElementTag
139	}
140
141	if v == nil {
142		if useGoXmlEmptyElemSyntax {
143			return []byte(prefix + "<" + rt + "></" + rt + ">"), nil
144		}
145		return []byte(prefix + "<" + rt + "/>"), nil
146	}
147	if reflect.TypeOf(v).Kind() == reflect.Struct {
148		return xml.MarshalIndent(v, prefix, indent)
149	}
150
151	var err error
152	s := new(bytes.Buffer)
153	p := new(pretty)
154	p.indent = indent
155	p.padding = prefix
156
157	var b []byte
158	switch v.(type) {
159	case []interface{}:
160		if _, err = s.WriteString("<" + rt + ">\n"); err != nil {
161			return nil, err
162		}
163		p.Indent()
164		for _, vv := range v.([]interface{}) {
165			switch vv.(type) {
166			case map[string]interface{}:
167				m := vv.(map[string]interface{})
168				if len(m) == 1 {
169					for tag, val := range m {
170						err = marshalMapToXmlIndent(true, s, tag, val, p)
171					}
172				} else {
173					p.start = 1 // we 1 tag in
174					err = marshalMapToXmlIndent(true, s, et, vv, p)
175					// *s += "\n"
176					if _, err = s.WriteString("\n"); err != nil {
177						return nil, err
178					}
179				}
180			default:
181				p.start = 0 // in case trailing p.start = 1
182				err = marshalMapToXmlIndent(true, s, et, vv, p)
183			}
184			if err != nil {
185				break
186			}
187		}
188		if _, err = s.WriteString(`</` + rt + `>`); err != nil {
189			return nil, err
190		}
191		b = s.Bytes()
192	case map[string]interface{}:
193		m := Map(v.(map[string]interface{}))
194		b, err = m.XmlIndent(prefix, indent, rt)
195	default:
196		err = marshalMapToXmlIndent(true, s, rt, v, p)
197		b = s.Bytes()
198	}
199
200	return b, err
201}
202