Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func main() {

### Implementation details ###

The main objective was to use standard encoding/xml package for XML marshalling/unmarshalling. Unfortunately, in current implementation there is no graceful way to implement common structre for marshal and unmarshal functions - marshalling doesn't handle interface{} types so far (though, it could be changed in the future).
The main objective was to use standard encoding/xml package for XML marshalling/unmarshalling. Unfortunately, in current implementation there is no graceful way to implement common structure for marshal and unmarshal functions - marshalling doesn't handle interface{} types so far (though, it could be changed in the future).
So, marshalling is implemented manually.

Unmarshalling code first creates temporary structure for unmarshalling XML into, then converts it into the passed variable using *reflect* package.
Expand All @@ -122,4 +122,3 @@ For the better understanding, I use terms 'rpc2xml' and 'xml2rpc' instead of 'ma
### TODO ###

* Add more corner cases tests

40 changes: 38 additions & 2 deletions xml/rpc2xml.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,48 @@ func struct2XML(value interface{}) (out string) {
field := reflect.ValueOf(value).Field(i)
field_type := reflect.TypeOf(value).Field(i)
var name string
if field_type.Tag.Get("xml") != "" {
name = field_type.Tag.Get("xml")
tag := field_type.Tag.Get("xml")
omit_empty := false
if tag != "" {
if strings.Contains(tag, "omitempty") {
omit_empty = true
val := strings.Split(tag, ",")
if len(val) == 1 || val[0] == "" {
//omit if empty but no field name defined, use struct default
name = field_type.Name
} else {
name = val[0]
}
} else {
name = tag
}
} else {
name = field_type.Name
}
field_value, _ := rpc2XML(field.Interface())
if omit_empty {
//from encoding/xml Marshal():
//empty values are false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero.
switch field_value {
case "<value><boolean>0</boolean></value>":
continue
case "<value><int>0</int></value>":
continue
case "<value><double>0</double></value>":
continue
case "<value><nil/></value>":
continue
case "<value><string></string></value>":
continue
case "<value><array><data></data></array></value>":
continue
case "<value><struct></struct></value>":
continue
default:
//assume there's not an empty base64 string nor empty time
//field_value = field_value
}
}
field_name := fmt.Sprintf("<name>%s</name>", name)
out += fmt.Sprintf("<member>%s%s</member>", field_name, field_value)
}
Expand Down
33 changes: 33 additions & 0 deletions xml/rpc2xml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,36 @@ func TestRpc2XmlNil(t *testing.T) {
t.Error("Got", xml)
}
}

type TaggedStructRpc2Xml struct {
Actual ActualTaggedStructRpc2Xml
}

type ActualTaggedStructRpc2Xml struct {
Foo string `xml:"other"`
Bar int
NonEmptyUnnamed string `xml:",omitempty"`
NonEmptyNamed string `xml:"emptiness,omitempty"`
EmptyString string `xml:",omitempty"`
EmptyBool bool `xml:"boo,omitempty"`
EmptyArray []interface{} `xml:"ari,omitempty"`
EmptyStruct struct{} `xml:"struck,omitempty"`
EmptyNum int `xml:"numi,omitempty"`
Nilly *int `xml:"nowhere,omitempty"`
}

var emptyStruct struct{}

func TestRPC2XmlTaggedStruct(t *testing.T) {
req := &TaggedStructRpc2Xml{ActualTaggedStructRpc2Xml{"testing", 123, "no tag name", "tag named", "", false, make([]interface{}, 0), emptyStruct, 0, nil}}
xml, err := rpcResponse2XML(req)
if err != nil {
t.Error("RPC2XML conversion failed", err)
}
expected := "<methodResponse><params><param><value><struct><member><name>other</name><value><string>testing</string></value></member><member><name>Bar</name><value><int>123</int></value></member><member><name>NonEmptyUnnamed</name><value><string>no tag name</string></value></member><member><name>emptiness</name><value><string>tag named</string></value></member></struct></value></param></params></methodResponse>"
if xml != expected {
t.Error("RPC2XML conversion of a tagged struct failed")
t.Error("Expected", expected)
t.Error("Got", xml)
}
}
2 changes: 1 addition & 1 deletion xml/xml2rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type value struct {
Boolean string `xml:"boolean"`
DateTime string `xml:"dateTime.iso8601"`
Base64 string `xml:"base64"`
Raw string `xml:",innerxml"` // the value can be defualt string
Raw string `xml:",innerxml"` // the value can be default string
}

type member struct {
Expand Down
33 changes: 33 additions & 0 deletions xml/xml2rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,36 @@ Requiredattribute'user'notfound:
}
}
}

/* This set of test code returns:

--- FAIL: TestXML2RPCTaggedStruct (0.00s)
xml2rpc_test.go:175: XML2RPC conversion to a tagged struct failed
xml2rpc_test.go:176: Expected &{{testing 123}}
xml2rpc_test.go:177: Got &{{ 123}}

Can't unmarshall XML/XML-RPC data/content to go struct where XML field name differs from go struct member name, via struct tag?

type TaggedStructXml2Rpc struct {
Actual ActualTaggedStructXml2Rpc
}

type ActualTaggedStructXml2Rpc struct {
Foo string `xml:"Other"`
Bar int
}

func TestXML2RPCTaggedStruct(t *testing.T) {
req := new(TaggedStructXml2Rpc)
err := xml2RPC("<methodCall><methodName>Some.Method</methodName><params><param><value><struct><member><name>Other</name><value><string>testing</string></value></member><member><name>Bar</name><value><int>123</int></value></member></struct></value></param></params></methodCall>", req)
if err != nil {
t.Error("XML2RPC conversion failed", err)
}
expected_req := &TaggedStructXml2Rpc{ActualTaggedStructXml2Rpc{"testing", 123}}
if !reflect.DeepEqual(req, expected_req) {
t.Error("XML2RPC conversion to a tagged struct failed")
t.Error("Expected", expected_req)
t.Error("Got", req)
}
}
*/