diff --git a/tools/types/json_array.go b/tools/types/json_array.go index a7749a9f..07ee6e9d 100644 --- a/tools/types/json_array.go +++ b/tools/types/json_array.go @@ -2,6 +2,7 @@ package types import ( "database/sql/driver" + "encoding/json/jsontext" "encoding/json/v2" "fmt" ) @@ -14,9 +15,13 @@ type jsonArrayAlias[T any] JSONArray[T] // MarshalJSON implements the [json.Marshaler] interface. func (m JSONArray[T]) MarshalJSON() ([]byte, error) { - // note: forces the Deterministic option to ensure consistent output - // in mixed json v1 and v2 configurations - return json.Marshal(jsonArrayAlias[T](m), json.Deterministic(true)) + // note: forces the Deterministic and AllowInvalidUTF8 options to + // ensure consistent output in mixed json v1 and v2 configurations + return json.Marshal( + jsonArrayAlias[T](m), + json.Deterministic(true), + jsontext.AllowInvalidUTF8(true), + ) } // String returns the string representation of the current json array. diff --git a/tools/types/json_array_test.go b/tools/types/json_array_test.go index 8527e083..d0a2be9c 100644 --- a/tools/types/json_array_test.go +++ b/tools/types/json_array_test.go @@ -18,7 +18,7 @@ func TestJSONArrayMarshalJSON(t *testing.T) { {types.JSONArray[any]{}, `[]`}, {types.JSONArray[int]{1, 2, 3}, `[1,2,3]`}, {types.JSONArray[string]{"test1", "test2", "test3"}, `["test1","test2","test3"]`}, - {types.JSONArray[any]{1, "test"}, `[1,"test"]`}, + {types.JSONArray[any]{1, "test\xc3" /* invalid utf8 char to test mangling */}, `[1,"test�"]`}, } for i, s := range scenarios { diff --git a/tools/types/json_map.go b/tools/types/json_map.go index f51eeeaa..bca05635 100644 --- a/tools/types/json_map.go +++ b/tools/types/json_map.go @@ -2,6 +2,7 @@ package types import ( "database/sql/driver" + "encoding/json/jsontext" "encoding/json/v2" "fmt" ) @@ -29,9 +30,13 @@ func (m JSONMap[T]) Set(key string, value T) { func (m JSONMap[T]) MarshalJSON() ([]byte, error) { type alias JSONMap[T] // prevent recursion - // note: forces the Deterministic option to ensure consistent output - // in mixed json v1 and v2 configurations - return json.Marshal(alias(m), json.Deterministic(true)) + // note: forces the Deterministic and AllowInvalidUTF8 options to + // ensure consistent output in mixed json v1 and v2 configurations + return json.Marshal( + alias(m), + json.Deterministic(true), + jsontext.AllowInvalidUTF8(true), + ) } // String returns the string representation of the current json map. diff --git a/tools/types/json_map_test.go b/tools/types/json_map_test.go index eab4b949..1102dfe6 100644 --- a/tools/types/json_map_test.go +++ b/tools/types/json_map_test.go @@ -16,8 +16,11 @@ func TestJSONMapMarshalJSON(t *testing.T) { }{ {nil, "{}"}, {types.JSONMap[any]{}, `{}`}, - {types.JSONMap[any]{"test1": 123, "test2": "lorem"}, `{"test1":123,"test2":"lorem"}`}, {types.JSONMap[any]{"test": []int{1, 2, 3}}, `{"test":[1,2,3]}`}, + { + types.JSONMap[any]{"test1": 123, "test2": "lorem\xc3" /* invalid utf8 char to test mangling */}, + `{"test1":123,"test2":"lorem�"}`, + }, } for i, s := range scenarios { diff --git a/tools/types/json_raw.go b/tools/types/json_raw.go index 129251f3..d228e41c 100644 --- a/tools/types/json_raw.go +++ b/tools/types/json_raw.go @@ -2,6 +2,7 @@ package types import ( "database/sql/driver" + "encoding/json/jsontext" "encoding/json/v2" "errors" ) @@ -73,7 +74,7 @@ func (j *JSONRaw) Scan(value any) error { data = []byte(v) } default: - bytes, err := json.Marshal(v, json.Deterministic(true)) + bytes, err := json.Marshal(v, json.Deterministic(true), jsontext.AllowInvalidUTF8(true)) if err != nil { return err } diff --git a/tools/types/json_raw_test.go b/tools/types/json_raw_test.go index 31c8eb25..384d39fe 100644 --- a/tools/types/json_raw_test.go +++ b/tools/types/json_raw_test.go @@ -167,7 +167,11 @@ func TestJSONRawScan(t *testing.T) { {`{"test":1}`, false, `{"test":1}`}, {[]byte(`[1,2,3]`), false, `[1,2,3]`}, {[]int{1, 2, 3}, false, `[1,2,3]`}, - {map[string]int{"test": 1}, false, `{"test":1}`}, + { + map[string]string{"test": "a\xc3" /* invalid utf8 char to test mangling */}, + false, + `{"test":"a�"}`, + }, } for i, s := range scenarios {