From 2ea570f2b291c8a6c0116e368c82f1bf6da3d3a7 Mon Sep 17 00:00:00 2001 From: Daniel Stinson-Diess Date: Wed, 27 Nov 2024 15:44:38 -0600 Subject: [PATCH] fix(message): use correct encodings for JSON Number values This updates the private setValue helper to conditonally use the correct float or integer encoding per JSON Number type. Relying on the gjson Value method can result in imprecise copies which caused incremented numbers on large integers like the test case added. --- message/message.go | 11 +++++++++++ transform/object_copy_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/message/message.go b/message/message.go index cc4d9d56..a3e4c746 100644 --- a/message/message.go +++ b/message/message.go @@ -5,6 +5,7 @@ import ( "bytes" "encoding/json" "fmt" + "math" "strings" "unicode/utf8" @@ -366,6 +367,16 @@ func setValue(json []byte, key string, value interface{}) ([]byte, error) { return sjson.SetBytes(json, key, base64.Encode(v)) } case Value: + // JSON number values can lose precision if not read with the right encoding. + // Determine if the value is an integer by checking if floating poit truncation has no + // affect of the value. + if v.gjson.Type == gjson.Number { + if v.Float() == math.Trunc(v.Float()) { + return sjson.SetBytes(json, key, v.Int()) + } + return sjson.SetBytes(json, key, v.Float()) + } + return sjson.SetBytes(json, key, v.Value()) default: return sjson.SetBytes(json, key, v) diff --git a/transform/object_copy_test.go b/transform/object_copy_test.go index d979b804..3be75a2f 100644 --- a/transform/object_copy_test.go +++ b/transform/object_copy_test.go @@ -32,6 +32,36 @@ var objectCopyTests = []struct { []byte(`{"a":"b","c":"b"}`), }, }, + { + "large integer", + config.Config{ + Settings: map[string]interface{}{ + "object": map[string]interface{}{ + "source_key": "a", + "target_key": "b", + }, + }, + }, + []byte(`{"a":30400402455622563}`), + [][]byte{ + []byte(`{"a":30400402455622563,"b":30400402455622563}`), + }, + }, + { + "large float", + config.Config{ + Settings: map[string]interface{}{ + "object": map[string]interface{}{ + "source_key": "a", + "target_key": "b", + }, + }, + }, + []byte(`{"a":3.141592653589793}`), + [][]byte{ + []byte(`{"a":3.141592653589793,"b":3.141592653589793}`), + }, + }, { "unescape object", config.Config{