From c68e01f0330aa3bc6efc94afd5e60b2e7b7bfecf Mon Sep 17 00:00:00 2001 From: Charles Banning Date: Thu, 26 Jul 2018 12:25:38 -0700 Subject: [PATCH] address issue #3 and #4 --- anyxml.go | 53 +++++++++++++++----------- nil_test.go | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++ xml.go | 4 +- xml_test.go | 49 ++++++++++++++++++++++++ 4 files changed, 187 insertions(+), 24 deletions(-) create mode 100644 nil_test.go create mode 100644 xml_test.go diff --git a/anyxml.go b/anyxml.go index 5e5d4a0..85ec6dd 100644 --- a/anyxml.go +++ b/anyxml.go @@ -1,6 +1,6 @@ // anyxml - marshal an XML document from almost any Go variable. -// Marshal XML from map[string]interface{}, arrays, slices, alpha/numeric, etc. -// +// 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 @@ -11,13 +11,13 @@ /* Encode an arbitrary JSON object. package main - + import ( "encoding/json" "fmt" "github.com/clbanning/anyxml" ) - + func main() { jsondata := []byte(`[ { "somekey":"somevalue" }, @@ -36,7 +36,7 @@ } fmt.Println(string(x)) } - + output: somevalue @@ -54,6 +54,18 @@ import ( // Encode arbitrary value as XML. Note: there are no guarantees. func Xml(v interface{}, rootTag ...string) ([]byte, error) { + var rt string + if len(rootTag) == 1 { + rt = rootTag[0] + } else { + rt = DefaultRootTag + } + if v == nil { + if UseGoEmptyElementSyntax { + return []byte("<" + rt + ">"), nil + } + return []byte("<" + rt + "/>"), nil + } if reflect.TypeOf(v).Kind() == reflect.Struct { return xml.Marshal(v) } @@ -62,13 +74,6 @@ func Xml(v interface{}, rootTag ...string) ([]byte, 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) { @@ -95,7 +100,7 @@ func Xml(v interface{}, rootTag ...string) ([]byte, error) { ss += *s + "" b = []byte(ss) case map[string]interface{}: - b, err = anyxml(v.(map[string]interface{}),rootTag...) + b, err = anyxml(v.(map[string]interface{}), rootTag...) default: err = mapToXmlIndent(false, s, rt, v, p) b = []byte(*s) @@ -104,9 +109,20 @@ func Xml(v interface{}, rootTag ...string) ([]byte, error) { 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) { + var rt string + if len(rootTag) == 1 { + rt = rootTag[0] + } else { + rt = DefaultRootTag + } + if v == nil { + if UseGoEmptyElementSyntax { + return []byte(prefix + "<" + rt + ">\n" + prefix + ""), nil + } + return []byte(prefix + "<" + rt + "/>"), nil + } if reflect.TypeOf(v).Kind() == reflect.Struct { return xml.MarshalIndent(v, prefix, indent) } @@ -117,13 +133,6 @@ func XmlIndent(v interface{}, prefix, indent string, rootTag ...string) ([]byte, 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) { @@ -154,7 +163,7 @@ func XmlIndent(v interface{}, prefix, indent string, rootTag ...string) ([]byte, ss += *s + "" b = []byte(ss) case map[string]interface{}: - b, err = anyxmlIndent(v.(map[string]interface{}),prefix, indent, rootTag...) + b, err = anyxmlIndent(v.(map[string]interface{}), prefix, indent, rootTag...) default: err = mapToXmlIndent(true, s, rt, v, p) b = []byte(*s) diff --git a/nil_test.go b/nil_test.go new file mode 100644 index 0000000..e0b3146 --- /dev/null +++ b/nil_test.go @@ -0,0 +1,105 @@ +package anyxml + +import ( + "fmt" + "testing" +) + +func TestNilHeader2(t *testing.T) { + fmt.Println("\n---------------- nil_test.go ...") +} + +func TestNilMap(t *testing.T) { + checkval := "" + xmlout, err := Xml(nil, "root") + if err != nil { + t.Fatal(err) + } + if string(xmlout) != checkval { + fmt.Println(string(xmlout), "!=", checkval) + t.Fatal() + } + + checkval = " " + xmlout, err = XmlIndent(nil, " ", " ", "root") + if err != nil { + t.Fatal(err) + } + if string(xmlout) != checkval { + fmt.Println(string(xmlout), "!=", checkval) + t.Fatal() + } + + // use Go XML marshal syntax for empty element" + UseGoEmptyElementSyntax = true + checkval = "" + xmlout, err = Xml(nil, "root") + if err != nil { + t.Fatal(err) + } + if string(xmlout) != checkval { + fmt.Println(string(xmlout), "!=", checkval) + t.Fatal() + } + + checkval = ` + ` + xmlout, err = XmlIndent(nil, " ", " ", "root") + if err != nil { + t.Fatal(err) + } + if string(xmlout) != checkval { + fmt.Println(string(xmlout), "!=", checkval) + t.Fatal() + } +} + +func TestNilValue(t *testing.T) { + val := map[string]interface{}{"toplevel": nil} + checkval := "" + + UseGoEmptyElementSyntax = false + xmlout, err := Xml(val, "root") + if err != nil { + t.Fatal(err) + } + if string(xmlout) != checkval { + fmt.Println(string(xmlout), "!=", checkval) + t.Fatal() + } + + checkval = ` + + ` + xmlout, err = XmlIndent(val, " ", " ", "root") + if err != nil { + t.Fatal(err) + } + if string(xmlout) != checkval { + fmt.Println(string(xmlout), "!=", checkval) + t.Fatal() + } + + UseGoEmptyElementSyntax = true + checkval = "" + xmlout, err = Xml(val, "root") + if err != nil { + t.Fatal(err) + } + if string(xmlout) != checkval { + fmt.Println(string(xmlout), "!=", checkval) + t.Fatal() + } + + checkval = ` + + ` + xmlout, err = XmlIndent(val, " ", " ", "root") + if err != nil { + t.Fatal(err) + } + if string(xmlout) != checkval { + fmt.Println(string(xmlout), "!=", checkval) + t.Fatal() + } +} diff --git a/xml.go b/xml.go index d053d9e..ea71857 100644 --- a/xml.go +++ b/xml.go @@ -19,7 +19,7 @@ import ( // --------------------------------- Xml, XmlIndent - from mxj ------------------------------- -const ( +var ( DefaultRootTag = "doc" UseGoEmptyElementSyntax = false // if 'true' encode empty element as "" instead of " ) @@ -363,7 +363,7 @@ func mapToXmlIndent(doIndent bool, s *string, key string, value interface{}, pp } } switch value.(type) { - case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32: + case map[string]interface{}, []byte, string, float64, bool, int, int32, int64, float32, nil: if elen > 0 || UseGoEmptyElementSyntax { if elen == 0 { *s += ">" diff --git a/xml_test.go b/xml_test.go new file mode 100644 index 0000000..5c4c439 --- /dev/null +++ b/xml_test.go @@ -0,0 +1,49 @@ +package anyxml + +import ( + "encoding/json" + "fmt" + "testing" +) + +func TestAnyXmlHeader2(t *testing.T) { + fmt.Println("\n---------------- xml_test.go ...") +} + +var anydata2 = []byte(`{ "element":[ + { + "somekey": "somevalue" + }, + { + "somekey": "somevalue" + }, + { + "somekey": "somevalue", + "someotherkey": "someothervalue" + }, + "a string", + 3.14159625, + true +]}`) + +func TestXml2(t *testing.T) { + var i interface{} + err := json.Unmarshal(anydata2, &i) + x, err := Xml(i) + if err != nil { + t.Fatal(err) + } + fmt.Println("[]->x:", string(x)) + +} + +func TestXmlIndent2(t *testing.T) { + var i interface{} + err := json.Unmarshal(anydata2, &i) + x, err := XmlIndent(i, "", " ") + if err != nil { + t.Fatal(err) + } + fmt.Println("[]->x:\n", string(x)) + +}