diff --git a/dtos/event.go b/dtos/event.go
index e4c8db2e..8b267597 100644
--- a/dtos/event.go
+++ b/dtos/event.go
@@ -1,5 +1,5 @@
//
-// Copyright (C) 2020-2021 IOTech Ltd
+// Copyright (C) 2020-2023 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0
@@ -7,8 +7,6 @@ package dtos
import (
"encoding/xml"
- "fmt"
- "strings"
"time"
"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/common"
@@ -21,13 +19,13 @@ import (
// https://app.swaggerhub.com/apis-docs/EdgeXFoundry1/core-data/2.1.0#/Event
type Event struct {
common.Versionable `json:",inline"`
- Id string `json:"id" validate:"required,uuid"`
- DeviceName string `json:"deviceName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
- ProfileName string `json:"profileName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
- SourceName string `json:"sourceName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
- Origin int64 `json:"origin" validate:"required"`
- Readings []BaseReading `json:"readings" validate:"gt=0,dive,required"`
- Tags map[string]interface{} `json:"tags,omitempty" xml:"-"` // Have to ignore since map not supported for XML
+ Id string `json:"id" validate:"required,uuid"`
+ DeviceName string `json:"deviceName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
+ ProfileName string `json:"profileName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
+ SourceName string `json:"sourceName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
+ Origin int64 `json:"origin" validate:"required"`
+ Readings []BaseReading `json:"readings" validate:"gt=0,dive,required"`
+ Tags Tags `json:"tags,omitempty"`
}
// NewEvent creates and returns an initialized Event with no Readings
@@ -93,19 +91,5 @@ func (e *Event) ToXML() (string, error) {
return "", err
}
- // The Tags field is being ignore from XML Marshaling since maps are not supported.
- // We have to provide our own marshaling of the Tags field if it is non-empty
- if len(e.Tags) > 0 {
- tagsXmlElements := []string{""}
- // Since we change the tags value from string to interface{}, we need to write more complex func or use third-party lib to handle the marshaling
- for key, value := range e.Tags {
- tag := fmt.Sprintf("<%s>%s%s>", key, value, key)
- tagsXmlElements = append(tagsXmlElements, tag)
- }
- tagsXmlElements = append(tagsXmlElements, "")
- tagsXml := strings.Join(tagsXmlElements, "")
- eventXml = []byte(strings.Replace(string(eventXml), "", tagsXml+"", 1))
- }
-
return string(eventXml), nil
}
diff --git a/dtos/reading.go b/dtos/reading.go
index 75d79e72..fdbc3aca 100644
--- a/dtos/reading.go
+++ b/dtos/reading.go
@@ -22,14 +22,14 @@ import (
// BaseReading and its properties are defined in the APIv2 specification:
// https://app.swaggerhub.com/apis-docs/EdgeXFoundry1/core-data/2.1.0#/BaseReading
type BaseReading struct {
- Id string `json:"id,omitempty"`
- Origin int64 `json:"origin" validate:"required"`
- DeviceName string `json:"deviceName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
- ResourceName string `json:"resourceName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
- ProfileName string `json:"profileName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
- ValueType string `json:"valueType" validate:"required,edgex-dto-value-type"`
- Units string `json:"units,omitempty"`
- Tags map[string]any `json:"tags,omitempty"`
+ Id string `json:"id,omitempty"`
+ Origin int64 `json:"origin" validate:"required"`
+ DeviceName string `json:"deviceName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
+ ResourceName string `json:"resourceName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
+ ProfileName string `json:"profileName" validate:"required,edgex-dto-rfc3986-unreserved-chars"`
+ ValueType string `json:"valueType" validate:"required,edgex-dto-value-type"`
+ Units string `json:"units,omitempty"`
+ Tags Tags `json:"tags,omitempty"`
BinaryReading `json:",inline" validate:"-"`
SimpleReading `json:",inline" validate:"-"`
ObjectReading `json:",inline" validate:"-"`
diff --git a/dtos/tags.go b/dtos/tags.go
new file mode 100644
index 00000000..01e10782
--- /dev/null
+++ b/dtos/tags.go
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2023 IOTech Ltd
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package dtos
+
+import "encoding/xml"
+
+type Tags map[string]any
+
+// MarshalXML fulfills the Marshaler interface for Tags field, which is being ignored
+// from XML Marshaling since maps are not supported. We have to provide our own
+// marshaling of the Tags field if it is non-empty.
+func (t Tags) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if len(t) == 0 {
+ return nil
+ }
+
+ err := e.EncodeToken(start)
+ if err != nil {
+ return err
+ }
+
+ for k, v := range t {
+ xmlMapEntry := struct {
+ XMLName xml.Name
+ Value any `xml:",chardata"`
+ }{
+ XMLName: xml.Name{Local: k},
+ Value: v,
+ }
+
+ err = e.Encode(xmlMapEntry)
+ if err != nil {
+ return err
+ }
+ }
+
+ return e.EncodeToken(start.End())
+}
diff --git a/dtos/tags_test.go b/dtos/tags_test.go
new file mode 100644
index 00000000..8d380426
--- /dev/null
+++ b/dtos/tags_test.go
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2023 IOTech Ltd
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package dtos
+
+import (
+ "encoding/xml"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestTags_MarshalXML(t *testing.T) {
+ testTags := Tags{
+ "String": "value",
+ "Numeric": 123,
+ "Bool": false,
+ }
+
+ xml, err := xml.Marshal(testTags)
+ require.NoError(t, err)
+
+ contains := []string{
+ "value",
+ "123",
+ "false"}
+
+ for _, v := range contains {
+ ok := strings.Contains(string(xml), v)
+ require.True(t, ok)
+ }
+}