anyxml.go raw

   1  package mxj
   2  
   3  import (
   4  	"bytes"
   5  	"encoding/xml"
   6  	"reflect"
   7  )
   8  
   9  const (
  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  
  55  An 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).
  59  func 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).
 128  func 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