Skip to content

Commit

Permalink
Fix json unmarshaling for numbers and enums
Browse files Browse the repository at this point in the history
Signed-off-by: Bogdan <[email protected]>
  • Loading branch information
bogdandrutu committed Aug 16, 2022
1 parent 15ac8f2 commit b4b1b48
Show file tree
Hide file tree
Showing 11 changed files with 438 additions and 200 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
- Add support to unmarshalls bytes into pmetric.Metrics with `jsoniter` in jsonUnmarshaler(#5433)
- Add httpprovider to allow loading config files stored in HTTP (#5810)
- Added `service.telemetry.traces.propagators` configuration to set propagators for collector's internal spans. (#5572)
- Fix json trace unmarshalling for numbers and enums (#5924):
- accept both string and number for int32/uint32.
- read uint64 numbers without converting from int64.
- avoid unnecessary allocation for reading enums.

### 🧰 Bug fixes 🧰

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,6 @@ func ReadAttribute(iter *jsoniter.Iterator) otlpcommon.KeyValue {
return kv
}

// ReadInt64 Unmarshal JSON data and return int64
func ReadInt64(iter *jsoniter.Iterator) int64 {
return iter.ReadAny().ToInt64()
}

func readAnyValue(iter *jsoniter.Iterator, f string) otlpcommon.AnyValue {
switch f {
case "stringValue", "string_value":
Expand Down
File renamed without changes.
33 changes: 33 additions & 0 deletions pdata/internal/json/enum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package json // import "go.opentelemetry.io/collector/pdata/internal/json"

import (
jsoniter "github.com/json-iterator/go"
)

// ReadEnumValue returns the enum integer value representation. Accepts both enum names and enum integer values.
// See https://developers.google.com/protocol-buffers/docs/proto3#json.
func ReadEnumValue(iter *jsoniter.Iterator, valueMap map[string]int32) int32 {
switch iter.WhatIsNext() {
case jsoniter.NumberValue:
return iter.ReadInt32()
case jsoniter.StringValue:
return valueMap[iter.ReadString()]
default:
iter.ReportError("ReadUint32", "unsupported value type")
return 0
}
}
80 changes: 80 additions & 0 deletions pdata/internal/json/enum_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package json

import (
"testing"

jsoniter "github.com/json-iterator/go"
"github.com/stretchr/testify/assert"
)

func TestReadEnumValue(t *testing.T) {
valueMap := map[string]int32{
"undefined": 0,
"foo": 1,
"bar": 2,
}
tests := []struct {
name string
jsonStr string
want int32
wantErr bool
}{
{
name: "foo string",
jsonStr: `"foo"`,
want: 1,
},
{
name: "foo number",
jsonStr: `1 `,
want: 1,
},
{
name: "bar string",
jsonStr: `"bar"`,
want: 2,
},
{
name: "bar number",
jsonStr: `2 `,
want: 2,
},
{
name: "number string",
jsonStr: `"2"`,
want: 0,
},
{
name: "wrong type",
jsonStr: `true`,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
iter := jsoniter.ConfigFastest.BorrowIterator([]byte(tt.jsonStr))
defer jsoniter.ConfigFastest.ReturnIterator(iter)
val := ReadEnumValue(iter, valueMap)
if tt.wantErr {
assert.Error(t, iter.Error)
return
}
assert.NoError(t, iter.Error)
assert.Equal(t, tt.want, val)
})
}
}
97 changes: 97 additions & 0 deletions pdata/internal/json/number.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package json // import "go.opentelemetry.io/collector/pdata/internal/json"

import (
"strconv"

jsoniter "github.com/json-iterator/go"
)

// ReadInt32 unmarshalls JSON data into an int32. Accepts both numbers and strings decimal.
// See https://developers.google.com/protocol-buffers/docs/proto3#json.
func ReadInt32(iter *jsoniter.Iterator) int32 {
switch iter.WhatIsNext() {
case jsoniter.NumberValue:
return iter.ReadInt32()
case jsoniter.StringValue:
val, err := strconv.ParseUint(iter.ReadString(), 10, 32)
if err != nil {
iter.ReportError("ReadInt32", err.Error())
return 0
}
return int32(val)
default:
iter.ReportError("ReadInt32", "unsupported value type")
return 0
}
}

// ReadUint32 unmarshalls JSON data into an uint32. Accepts both numbers and strings decimal.
// See https://developers.google.com/protocol-buffers/docs/proto3#json.
func ReadUint32(iter *jsoniter.Iterator) uint32 {
switch iter.WhatIsNext() {
case jsoniter.NumberValue:
return iter.ReadUint32()
case jsoniter.StringValue:
val, err := strconv.ParseUint(iter.ReadString(), 10, 32)
if err != nil {
iter.ReportError("ReadUint32", err.Error())
return 0
}
return uint32(val)
default:
iter.ReportError("ReadUint32", "unsupported value type")
return 0
}
}

// ReadInt64 unmarshalls JSON data into an int64. Accepts both numbers and strings decimal.
// See https://developers.google.com/protocol-buffers/docs/proto3#json.
func ReadInt64(iter *jsoniter.Iterator) int64 {
switch iter.WhatIsNext() {
case jsoniter.NumberValue:
return iter.ReadInt64()
case jsoniter.StringValue:
val, err := strconv.ParseInt(iter.ReadString(), 10, 64)
if err != nil {
iter.ReportError("ReadInt64", err.Error())
return 0
}
return val
default:
iter.ReportError("ReadInt64", "unsupported value type")
return 0
}
}

// ReadUint64 unmarshalls JSON data into an uint64. Accepts both numbers and strings decimal.
// See https://developers.google.com/protocol-buffers/docs/proto3#json.
func ReadUint64(iter *jsoniter.Iterator) uint64 {
switch iter.WhatIsNext() {
case jsoniter.NumberValue:
return iter.ReadUint64()
case jsoniter.StringValue:
val, err := strconv.ParseUint(iter.ReadString(), 10, 64)
if err != nil {
iter.ReportError("ReadUint64", err.Error())
return 0
}
return val
default:
iter.ReportError("ReadUint64", "unsupported value type")
return 0
}
}
Loading

0 comments on commit b4b1b48

Please sign in to comment.