Skip to content

Commit

Permalink
feat: implement xml Marshaler interface for event/reading tags
Browse files Browse the repository at this point in the history
Signed-off-by: Chris Hung <[email protected]>
  • Loading branch information
Chris Hung committed Jan 18, 2023
1 parent 2b7b082 commit 809b610
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 32 deletions.
32 changes: 8 additions & 24 deletions dtos/event.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
//
// Copyright (C) 2020-2021 IOTech Ltd
// Copyright (C) 2020-2023 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0

package dtos

import (
"encoding/xml"
"fmt"
"strings"
"time"

"github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/common"
Expand All @@ -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
Expand Down Expand Up @@ -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{"<Tags>"}
// 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, "</Tags>")
tagsXml := strings.Join(tagsXmlElements, "")
eventXml = []byte(strings.Replace(string(eventXml), "</Event>", tagsXml+"</Event>", 1))
}

return string(eventXml), nil
}
16 changes: 8 additions & 8 deletions dtos/reading.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:"-"`
Expand Down
41 changes: 41 additions & 0 deletions dtos/tags.go
Original file line number Diff line number Diff line change
@@ -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())
}
35 changes: 35 additions & 0 deletions dtos/tags_test.go
Original file line number Diff line number Diff line change
@@ -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{
"<String>value</String>",
"<Numeric>123</Numeric>",
"<Bool>false</Bool>"}

for _, v := range contains {
ok := strings.Contains(string(xml), v)
require.True(t, ok)
}
}

0 comments on commit 809b610

Please sign in to comment.