Fixing unmarshalling of numberOrString

New unmarshalling rules for fields which could be numeric or string
values were not properly typecasted upon being deserialized.

Cleaned up the api template file and moved custom unmarshalling type
logic into go code out of the template.
This commit is contained in:
James Stephenson
2021-04-16 09:47:21 -04:00
committed by Paul Tyng
parent 4ab4036985
commit fbed685c37
4 changed files with 48 additions and 26 deletions

View File

@@ -2,22 +2,19 @@
{{ define "field" }} {{ define "field" }}
{{ .FieldName }} {{ if .IsArray }}[]{{end}}{{ .FieldType }} `json:"{{ .JSONName }}{{ if .OmitEmpty }},omitempty{{ end }}"` {{ if .FieldValidation }}// {{ .FieldValidation }}{{ end }} {{- end }} {{ .FieldName }} {{ if .IsArray }}[]{{end}}{{ .FieldType }} `json:"{{ .JSONName }}{{ if .OmitEmpty }},omitempty{{ end }}"` {{ if .FieldValidation }}// {{ .FieldValidation }}{{ end }} {{- end }}
{{ define "field-emptyStringInt" }} {{ define "field-customUnmarshalType" }}
{{- if ne .FieldType "int" }}{{else}} {{- if eq .CustomUnmarshalType "" }}{{else}}
{{ .FieldName }} {{ if .IsArray }}[]{{end}}emptyStringInt `json:"{{ .JSONName }}{{ if .OmitEmpty }}{{ end }}"`{{ end }} {{- end }} {{ .FieldName }} {{ if .IsArray }}[]{{end}}{{ .CustomUnmarshalType }} `json:"{{ .JSONName }}{{ if .OmitEmpty }}{{ end }}"`{{ end }} {{- end }}
{{ define "field-numberOrString" }}
{{- /* this is kind of a hack, probably a better way to do this when looking at the normalized validation */ -}}
{{- if and (eq .FieldType "string") (or (eq .JSONName "channel") (eq .JSONName "backup_channel") (eq .JSONName "tx_power")) }}
{{ .FieldName }} {{ if .IsArray }}[]{{end}}numberOrString `json:"{{ .JSONName }}{{ if .OmitEmpty }}{{ end }}"`{{ end }} {{- end }}
{{ define "typecast" }} {{ define "typecast" }}
{{- if eq .FieldType "int" }}{{- if .IsArray }} {{- if eq .CustomUnmarshalType "" }}{{else}}
dst.{{ .FieldName }}= make([]int, len(aux.{{ .FieldName }})) {{- if .IsArray }}
for i, v := range aux.{{ .FieldName }} { dst.{{ .FieldName }}= make([]{{ .FieldType }}, len(aux.{{ .FieldName }}))
dst.{{ .FieldName }}[i] = int(v) for i, v := range aux.{{ .FieldName }} {
} dst.{{ .FieldName }}[i] = {{ .FieldType }}(v)
{{- else }} }
dst.{{ .FieldName }} = int(aux.{{ .FieldName }}) {{- else }}
{{- end }}{{- end }}{{- end }} dst.{{ .FieldName }} = {{ .FieldType }}(aux.{{ .FieldName }})
{{- end }}{{- end }}{{- end }}
{{ define "field-embed" }} {{ define "field-embed" }}
{{ .FieldName }} {{ if .IsArray }}[]{{end}}{{ if not .Fields }}{{ .FieldType }}{{ else }}struct { {{ .FieldName }} {{ if .IsArray }}[]{{end}}{{ if not .Fields }}{{ .FieldType }}{{ else }}struct {
{{ range $fk, $fv := .Fields }}{{ if not $fv }} {{ range $fk, $fv := .Fields }}{{ if not $fv }}
@@ -58,8 +55,8 @@ type {{ $k }} struct {
func (dst *{{ $k }}) UnmarshalJSON(b []byte) error { func (dst *{{ $k }}) UnmarshalJSON(b []byte) error {
type Alias {{ $k }} type Alias {{ $k }}
aux := &struct { aux := &struct {
{{- range $fk, $fv := $v.Fields }}{{ if not $fv }} {{- range $fk, $fv := $v.Fields }}{{ if not $fv }}
{{- else }}{{- template "field-emptyStringInt" $fv }}{{- template "field-numberOrString" $fv }}{{ end }}{{- end }} {{- else }}{{- template "field-customUnmarshalType" $fv }}{{ end }}{{- end }}
*Alias *Alias
}{ }{
@@ -71,8 +68,8 @@ func (dst *{{ $k }}) UnmarshalJSON(b []byte) error {
return fmt.Errorf("unable to unmarshal alias: %w", err) return fmt.Errorf("unable to unmarshal alias: %w", err)
} }
{{- range $fk, $fv := $v.Fields }}{{ if not $fv }} {{- range $fk, $fv := $v.Fields }}{{ if not $fv }}
{{- else }}{{- template "typecast" $fv }}{{ end }}{{ end }} {{- else }}{{- template "typecast" $fv }}{{ end }}{{ end }}
return nil return nil
} }

View File

@@ -98,13 +98,14 @@ type Resource struct {
} }
type FieldInfo struct { type FieldInfo struct {
FieldName string FieldName string
JSONName string JSONName string
FieldType string FieldType string
FieldValidation string FieldValidation string
OmitEmpty bool OmitEmpty bool
IsArray bool IsArray bool
Fields map[string]*FieldInfo Fields map[string]*FieldInfo
CustomUnmarshalType string
} }
func NewResource(structName string, resourcePath string) *Resource { func NewResource(structName string, resourcePath string) *Resource {
@@ -278,6 +279,16 @@ func main() {
} }
return nil return nil
} }
case "ChannelPlan":
resource.FieldProcessor = func(name string, f *FieldInfo) error {
switch name {
case "Channel", "BackupChannel", "TxPower":
if f.FieldType == "string" {
f.CustomUnmarshalType = "numberOrString"
}
}
return nil
}
case "Device": case "Device":
resource.FieldProcessor = func(name string, f *FieldInfo) error { resource.FieldProcessor = func(name string, f *FieldInfo) error {
switch name { switch name {
@@ -285,6 +296,11 @@ func main() {
f.FieldType = "float64" f.FieldType = "float64"
case "StpPriority", "Ht": case "StpPriority", "Ht":
f.FieldType = "string" f.FieldType = "string"
f.CustomUnmarshalType = ""
case "Channel", "BackupChannel", "TxPower":
if f.FieldType == "string" {
f.CustomUnmarshalType = "numberOrString"
}
} }
f.OmitEmpty = true f.OmitEmpty = true
@@ -294,6 +310,7 @@ func main() {
resource.FieldProcessor = func(name string, f *FieldInfo) error { resource.FieldProcessor = func(name string, f *FieldInfo) error {
if strings.HasSuffix(name, "Timeout") && name != "ArpCacheTimeout" { if strings.HasSuffix(name, "Timeout") && name != "ArpCacheTimeout" {
f.FieldType = "int" f.FieldType = "int"
f.CustomUnmarshalType = "emptyStringInt"
} }
return nil return nil
} }
@@ -304,6 +321,7 @@ func main() {
f.FieldType = "bool" f.FieldType = "bool"
case "LastSeen": case "LastSeen":
f.FieldType = "int" f.FieldType = "int"
f.CustomUnmarshalType = "emptyStringInt"
} }
return nil return nil
} }
@@ -416,6 +434,7 @@ func (r *Resource) fieldInfoFromValidation(name string, validation interface{})
omitEmpty = true omitEmpty = true
fieldInfo, err = NewFieldInfo(fieldName, name, "int", fieldValidation, omitEmpty, false), nil fieldInfo, err = NewFieldInfo(fieldName, name, "int", fieldValidation, omitEmpty, false), nil
fieldInfo.CustomUnmarshalType = "emptyStringInt"
return fieldInfo, r.FieldProcessor(fieldName, fieldInfo) return fieldInfo, r.FieldProcessor(fieldName, fieldInfo)
} }
} }

View File

@@ -133,6 +133,9 @@ func (dst *ChannelPlanRadioTable) UnmarshalJSON(b []byte) error {
if err != nil { if err != nil {
return fmt.Errorf("unable to unmarshal alias: %w", err) return fmt.Errorf("unable to unmarshal alias: %w", err)
} }
dst.BackupChannel = string(aux.BackupChannel)
dst.Channel = string(aux.Channel)
dst.TxPower = string(aux.TxPower)
dst.Width = int(aux.Width) dst.Width = int(aux.Width)
return nil return nil

View File

@@ -315,9 +315,12 @@ func (dst *DeviceRadioTable) UnmarshalJSON(b []byte) error {
} }
dst.AntennaGain = int(aux.AntennaGain) dst.AntennaGain = int(aux.AntennaGain)
dst.AntennaID = int(aux.AntennaID) dst.AntennaID = int(aux.AntennaID)
dst.BackupChannel = string(aux.BackupChannel)
dst.Channel = string(aux.Channel)
dst.Maxsta = int(aux.Maxsta) dst.Maxsta = int(aux.Maxsta)
dst.MinRssi = int(aux.MinRssi) dst.MinRssi = int(aux.MinRssi)
dst.SensLevel = int(aux.SensLevel) dst.SensLevel = int(aux.SensLevel)
dst.TxPower = string(aux.TxPower)
return nil return nil
} }