Skip to content

JSON encoder for Number values produces duplicate fields #596

@tfinlay-lightspeed

Description

@tfinlay-lightspeed

Description

The JSON encoder's encodeScalar method has a special case for encoding Number values. This case doesn't return so execution continues to encode the value as a string as well.

This causes the same field to be encoded twice, introducing duplicate fields on the JSON object and causing errors when unmarshalling.

Relevant section of the encodeScalar method

func (e *Encoder) encodeScalar(vp valueProvider, rv reflect.Value) error {
if rv.Type() == serde.ReflectTypeOf.DocumentNumber {
number := rv.Interface().(document.Number)
if !isValidJSONNumber(number.String()) {
return &document.InvalidMarshalError{Message: fmt.Sprintf("invalid number literal: %s", number)}
}
vp.GetValue().Write([]byte(number))
}
switch rv.Kind() {
case reflect.Bool:
vp.GetValue().Boolean(rv.Bool())
case reflect.String:
vp.GetValue().String(rv.String())

Impact

I have a use-case where I receive and unmarshal a document containing Number values, and then pass that document back to the API again. Unfortunately this issue causes that second API call to error because of the malformed JSON input!

Reproduction

package main

import (
	"testing"

	smithydocument "github.com/aws/smithy-go/document"
	smithydocumentjson "github.com/aws/smithy-go/document/json"
)

func TestEncodeNumber(t *testing.T) {
	res, err := smithydocumentjson.NewEncoder().Encode(map[string]interface{}{
		// Example of tool call parameters unmarshalled into map[string]interface{}
		// Smithy encodes this parameter twice - once as a JSON number and once as a JSON string.
		// https://github.com/aws/smithy-go/blob/16d913d93001992645c0f54e3ce9fa6be287b70a/document/json/encoder.go#L217-L230
		"x": smithydocument.Number("2"),
	})

	if err != nil {
		t.Fatalf("MarshalSmithyDocument returned error: %v", err)
	}
	expected := `{"x":2}`
	if string(res) != expected {
		t.Errorf("Expected JSON output to be %s, got %s", expected, string(res))
	}
}

Produces:

=== RUN   TestEncodeNumber
    smithy_test.go:23: Expected JSON output to be {"x":2}, got {"x":2,"x":"2"}
--- FAIL: TestEncodeNumber (0.00s)

FAIL

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions