diff --git a/soap/soap_test.go b/soap/soap_test.go
index 0115773..b436df5 100644
--- a/soap/soap_test.go
+++ b/soap/soap_test.go
@@ -369,6 +369,10 @@ func TestXsdDateTime(t *testing.T) {
XMLName xml.Name `xml:"TestDateTime"`
Datetime XSDDateTime
}
+ type TestAttrDateTime struct {
+ XMLName xml.Name `xml:"TestAttrDateTime"`
+ Datetime XSDDateTime `xml:"Datetime,attr"`
+ }
// test marshalling
{
// without nanosecond
@@ -433,6 +437,22 @@ func TestXsdDateTime(t *testing.T) {
}
}
+ // test marshalling as attribute
+ {
+ testDateTime := TestAttrDateTime{
+ Datetime: CreateXsdDateTime(time.Date(1951, time.October, 22, 1, 2, 3, 4, time.UTC), true),
+ }
+ if output, err := xml.MarshalIndent(testDateTime, "", ""); err != nil {
+ t.Error(err)
+ } else {
+ outputstr := string(output)
+ expected := ""
+ if outputstr != expected {
+ t.Errorf("Got: %v\nExpected: %v", outputstr, expected)
+ }
+ }
+ }
+
// test unmarshalling
{
dateTimes := map[string]time.Time{
@@ -451,6 +471,25 @@ func TestXsdDateTime(t *testing.T) {
}
}
}
+
+ // test unmarshalling as attribute
+ {
+ dateTimes := map[string]time.Time{
+ "": time.Date(1951, time.October, 22, 1, 2, 3, 4, time.FixedZone("-0800", -8*60*60)),
+ "": time.Date(1951, time.October, 22, 1, 2, 3, 0, time.UTC),
+ "": time.Date(1951, time.October, 22, 1, 2, 3, 0, time.Local),
+ }
+ for dateTimeStr, dateTimeObj := range dateTimes {
+ parsedDt := TestAttrDateTime{}
+ if err := xml.Unmarshal([]byte(dateTimeStr), &parsedDt); err != nil {
+ t.Error(err)
+ } else {
+ if !parsedDt.Datetime.ToGoTime().Equal(dateTimeObj) {
+ t.Errorf("Got: %#v\nExpected: %#v", parsedDt.Datetime.ToGoTime(), dateTimeObj)
+ }
+ }
+ }
+ }
}
// TestXsdDateTime checks the marshalled xsd datetime
@@ -459,6 +498,10 @@ func TestXsdDate(t *testing.T) {
XMLName xml.Name `xml:"TestDate"`
Date XSDDate
}
+ type TestAttrDate struct {
+ XMLName xml.Name `xml:"TestAttrDate"`
+ Date XSDDate `xml:"Date,attr"`
+ }
// test marshalling
{
@@ -508,6 +551,22 @@ func TestXsdDate(t *testing.T) {
}
}
+ // test marshalling as attribute
+ {
+ testDate := TestAttrDate{
+ Date: CreateXsdDate(time.Date(1951, time.October, 22, 0, 0, 0, 0, time.UTC), true),
+ }
+ if output, err := xml.MarshalIndent(testDate, "", ""); err != nil {
+ t.Error(err)
+ } else {
+ outputstr := string(output)
+ expected := ""
+ if outputstr != expected {
+ t.Errorf("Got: %v\nExpected: %v", outputstr, expected)
+ }
+ }
+ }
+
// test unmarshalling
{
dates := map[string]time.Time{
@@ -526,6 +585,25 @@ func TestXsdDate(t *testing.T) {
}
}
}
+
+ // test unmarshalling as attribute
+ {
+ dates := map[string]time.Time{
+ "": time.Date(1951, time.October, 22, 0, 0, 0, 0, time.Local),
+ "": time.Date(1951, time.October, 22, 0, 0, 0, 0, time.UTC),
+ "": time.Date(1951, time.October, 22, 0, 0, 0, 0, time.FixedZone("UTC-8", -8*60*60)),
+ }
+ for dateStr, dateObj := range dates {
+ parsedDate := TestAttrDate{}
+ if err := xml.Unmarshal([]byte(dateStr), &parsedDate); err != nil {
+ t.Error(dateStr, err)
+ } else {
+ if !parsedDate.Date.ToGoTime().Equal(dateObj) {
+ t.Errorf("Got: %#v\nExpected: %#v", parsedDate.Date.ToGoTime(), dateObj)
+ }
+ }
+ }
+ }
}
// TestXsdTime checks the marshalled xsd datetime
@@ -534,6 +612,10 @@ func TestXsdTime(t *testing.T) {
XMLName xml.Name `xml:"TestTime"`
Time XSDTime
}
+ type TestAttrTime struct {
+ XMLName xml.Name `xml:"TestAttrTime"`
+ Time XSDTime `xml:"Time,attr"`
+ }
// test marshalling
{
@@ -578,6 +660,21 @@ func TestXsdTime(t *testing.T) {
}
}
}
+ // test marshalling as attribute
+ {
+ testTime := TestAttrTime{
+ Time: CreateXsdTime(12, 13, 14, 4, time.FixedZone("Test", -19800)),
+ }
+ if output, err := xml.MarshalIndent(testTime, "", ""); err != nil {
+ t.Error(err)
+ } else {
+ outputstr := string(output)
+ expected := ""
+ if outputstr != expected {
+ t.Errorf("Got: %v\nExpected: %v", outputstr, expected)
+ }
+ }
+ }
// test unmarshalling without TZ
{
@@ -652,6 +749,30 @@ func TestXsdTime(t *testing.T) {
}
}
}
+ // test unmarshalling as attribute
+ {
+ timeStr := ""
+ parsedTime := TestAttrTime{}
+ if err := xml.Unmarshal([]byte(timeStr), &parsedTime); err != nil {
+ t.Error(err)
+ } else {
+ if parsedTime.Time.Hour() != 12 {
+ t.Errorf("Got hour %#v\nExpected: %#v", parsedTime.Time.Hour(), 12)
+ }
+ if parsedTime.Time.Minute() != 13 {
+ t.Errorf("Got minute %#v\nExpected: %#v", parsedTime.Time.Minute(), 13)
+ }
+ if parsedTime.Time.Second() != 14 {
+ t.Errorf("Got second %#v\nExpected: %#v", parsedTime.Time.Second(), 14)
+ }
+ if parsedTime.Time.Nanosecond() != 0 {
+ t.Errorf("Got nsec %#v\nExpected: %#v", parsedTime.Time.Nanosecond(), 0)
+ }
+ if parsedTime.Time.Location().String() != "UTC" {
+ t.Errorf("Got location %v\nExpected: UTC", parsedTime.Time.Location().String())
+ }
+ }
+ }
}
func TestHTTPError(t *testing.T) {
diff --git a/soap/xsdDateTime.go b/soap/xsdDateTime.go
index f297daa..375bdae 100644
--- a/soap/xsdDateTime.go
+++ b/soap/xsdDateTime.go
@@ -41,8 +41,28 @@ func (xdt *XSDDateTime) ToGoTime() time.Time {
xdt.innerTime.Nanosecond(), time.Local)
}
-// MarshalXML implementation on DateTime to skip "zero" time values. It also checks if nanoseconds and TZ exist.
+// MarshalXML implements xml.MarshalerAttr on XSDDateTime
func (xdt XSDDateTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ xdtString := xdt.string()
+ if xdtString != "" {
+ return e.EncodeElement(xdtString, start)
+ }
+ return nil
+}
+
+// MarshalXMLAttr implements xml.MarshalerAttr on XSDDateTime
+func (xdt XSDDateTime) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
+ xdtString := xdt.string()
+ attr := xml.Attr{}
+ if xdtString != "" {
+ attr.Name = name
+ attr.Value = xdtString
+ }
+ return attr, nil
+}
+
+// returns string representation and skips "zero" time values. It also checks if nanoseconds and TZ exist.
+func (xdt XSDDateTime) string() string {
if !xdt.innerTime.IsZero() {
dateTimeLayout := time.RFC3339Nano
if xdt.innerTime.Nanosecond() == 0 {
@@ -57,33 +77,31 @@ func (xdt XSDDateTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error
toks = strings.SplitN(toks[0], "-", 2)
dtString = dateAndTime[0] + "T" + toks[0]
}
- e.EncodeElement(dtString, start)
+ return dtString
}
- return nil
+ return ""
}
-// UnmarshalXML implementation on DateTimeg to use dateTimeLayout
+// UnmarshalXML implements xml.Unmarshaler on XSDDateTime to use time.RFC3339Nano
func (xdt *XSDDateTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
- var err error
- xdt.innerTime, xdt.hasTz, err = unmarshalTime(d, start, time.RFC3339Nano)
+ var content string
+ err := d.DecodeElement(&content, &start)
+ if err != nil {
+ return err
+ }
+ xdt.innerTime, xdt.hasTz, err = fromString(content, time.RFC3339Nano)
return err
}
-// CreateXsdDateTime creates an object represent xsd:datetime object in Golang
-func CreateXsdDateTime(dt time.Time, hasTz bool) XSDDateTime {
- return XSDDateTime{
- innerTime: dt,
- hasTz: hasTz,
- }
+// UnmarshalXMLAttr implements xml.UnmarshalerAttr on XSDDateTime to use time.RFC3339Nano
+func (xdt *XSDDateTime) UnmarshalXMLAttr(attr xml.Attr) error {
+ var err error
+ xdt.innerTime, xdt.hasTz, err = fromString(attr.Value, time.RFC3339Nano)
+ return err
}
-func unmarshalTime(d *xml.Decoder, start xml.StartElement, format string) (time.Time, bool, error) {
+func fromString(content string, format string) (time.Time, bool, error) {
var t time.Time
- var content string
- err := d.DecodeElement(&content, &start)
- if err != nil {
- return t, true, err
- }
if content == "" {
return t, true, nil
}
@@ -114,8 +132,16 @@ func unmarshalTime(d *xml.Decoder, start xml.StartElement, format string) (time.
content += "Z"
}
}
- t, err = time.Parse(format, content)
- return t, hasTz, nil
+ t, err := time.Parse(format, content)
+ return t, hasTz, err
+}
+
+// CreateXsdDateTime creates an object represent xsd:datetime object in Golang
+func CreateXsdDateTime(dt time.Time, hasTz bool) XSDDateTime {
+ return XSDDateTime{
+ innerTime: dt,
+ hasTz: hasTz,
+ }
}
// XSDDate is a type for representing xsd:date in Golang
@@ -139,8 +165,28 @@ func (xd *XSDDate) ToGoTime() time.Time {
0, 0, 0, 0, time.Local)
}
-// MarshalXML implementation on DateTimeg to skip "zero" time values
+// MarshalXML implementation on XSDDate
func (xd XSDDate) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ xdtString := xd.string()
+ if xdtString != "" {
+ return e.EncodeElement(xdtString, start)
+ }
+ return nil
+}
+
+// MarshalXMLAttr implementation on XSDDate
+func (xd XSDDate) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
+ xdString := xd.string()
+ attr := xml.Attr{}
+ if xdString != "" {
+ attr.Name = name
+ attr.Value = xdString
+ }
+ return attr, nil
+}
+
+// returns string representation and skips "zero" time values
+func (xd XSDDate) string() string {
if !xd.innerDate.IsZero() {
dateString := xd.innerDate.Format(dateLayout) // serialize with TZ
if !xd.hasTz {
@@ -156,15 +202,26 @@ func (xd XSDDate) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
}
}
}
- e.EncodeElement(dateString, start)
+ return dateString
}
- return nil
+ return ""
}
-// UnmarshalXML implementation on DateTimeg to use dateTimeLayout
+// UnmarshalXML implements xml.Unmarshaler on XSDDate to use dateLayout
func (xd *XSDDate) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ var content string
+ err := d.DecodeElement(&content, &start)
+ if err != nil {
+ return err
+ }
+ xd.innerDate, xd.hasTz, err = fromString(content, dateLayout)
+ return err
+}
+
+// UnmarshalXMLAttr implements xml.UnmarshalerAttr on XSDDate to use dateLayout
+func (xd *XSDDate) UnmarshalXMLAttr(attr xml.Attr) error {
var err error
- xd.innerDate, xd.hasTz, err = unmarshalTime(d, start, dateLayout)
+ xd.innerDate, xd.hasTz, err = fromString(attr.Value, dateLayout)
return err
}
@@ -182,8 +239,28 @@ type XSDTime struct {
hasTz bool
}
-// MarshalXML implementation on DateTimeg to skip "zero" time values
+// MarshalXML implements xml.Marshaler on XSDTime
func (xt XSDTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ xdtString := xt.string()
+ if xdtString != "" {
+ return e.EncodeElement(xdtString, start)
+ }
+ return nil
+}
+
+// MarshalXMLAttr implements xml.MarshalerAttr on XSDTime
+func (xt XSDTime) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
+ xdString := xt.string()
+ attr := xml.Attr{}
+ if xdString != "" {
+ attr.Name = name
+ attr.Value = xdString
+ }
+ return attr, nil
+}
+
+// returns string representation and skips "zero" time values
+func (xt XSDTime) string() string {
if !xt.innerTime.IsZero() {
dateTimeLayout := time.RFC3339Nano
if xt.innerTime.Nanosecond() == 0 {
@@ -198,20 +275,30 @@ func (xt XSDTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
toks = strings.SplitN(toks[0], "-", 2)
timeString = toks[0]
}
- e.EncodeElement(timeString, start)
+ return timeString
}
- return nil
+ return ""
}
-// UnmarshalXML implementation on DateTimeg to use dateTimeLayout
+// UnmarshalXML implements xml.Unmarshaler on XSDTime to use dateTimeLayout
func (xt *XSDTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
- var t time.Time
var err error
var content string
err = d.DecodeElement(&content, &start)
if err != nil {
return err
}
+ return xt.fromString(content)
+}
+
+// UnmarshalXMLAttr implements xml.UnmarshalerAttr on XSDTime to use dateTimeLayout
+func (xt *XSDTime) UnmarshalXMLAttr(attr xml.Attr) error {
+ return xt.fromString(attr.Value)
+}
+
+func (xt *XSDTime) fromString(content string) error {
+ var t time.Time
+ var err error
if content == "" {
xt.innerTime = t
return nil