diff --git a/vlib/x/json2/encoder.v b/vlib/x/json2/encoder.v index 179f7f655604ac..4efb86f9df5484 100644 --- a/vlib/x/json2/encoder.v +++ b/vlib/x/json2/encoder.v @@ -4,6 +4,8 @@ module json2 import time +import math +import strconv // Encoder encodes the an `Any` type into JSON representation. // It provides parameters in order to change the end result. @@ -184,9 +186,12 @@ fn (e &Encoder) encode_value_with_level[T](val T, level int, mut buf []u8) ! { } $else $if T is $enum { str_int := int(val).str() unsafe { buf.push_many(str_int.str, str_int.len) } - } $else $if T is $int || T is $float || T is bool { + } $else $if T is $int || T is bool { str_int := val.str() unsafe { buf.push_many(str_int.str, str_int.len) } + } $else $if T is $float { + str_float := encode_number(val) + unsafe { buf.push_many(str_float.str, str_float.len) } } $else { return error('cannot encode value with ${typeof(val).name} type') } @@ -305,9 +310,12 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut buf []u8) ! { } else { unsafe { buf.push_many(false_in_string.str, false_in_string.len) } } - } $else $if field.typ in [$float, $int] { + } $else $if field.typ is $int { str_value := val.$(field.name).str() unsafe { buf.push_many(str_value.str, str_value.len) } + } $else $if field.typ is $float { + str_value := encode_number(val.$(field.name)) + unsafe { buf.push_many(str_value.str, str_value.len) } } $else $if field.is_array { // TODO: replace for `field.typ is $array` e.encode_array(value, level + 1, mut buf)! @@ -344,9 +352,12 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut buf []u8) ! { } else { unsafe { buf.push_many(false_in_string.str, false_in_string.len) } } - } $else $if field.unaliased_typ in [$float, $int] { + } $else $if field.unaliased_typ is $int { str_value := val.$(field.name).str() unsafe { buf.push_many(str_value.str, str_value.len) } + } $else $if field.unaliased_typ is $float { + str_value := encode_number(val) + unsafe { buf.push_many(str_value.str, str_value.len) } } $else $if field.unaliased_typ is $array { // TODO } $else $if field.unaliased_typ is $struct { @@ -581,3 +592,16 @@ fn hex_digit(n int) u8 { } return `a` + (n - 10) } + +fn encode_number(value f64) string { + if math.is_nan(value) || math.is_inf(value, 0) { + return 'null' + } else if value == f64(int(value)) { + return int(value).str() + } else { + // TODO:cjson Try 15 decimal places of precision to avoid nonsignificant nonzero digits + // If not, print with 17 decimal places of precision + // strconv.f64_to_str_l try max 18 digits instead. + return strconv.f64_to_str_l(value) + } +} diff --git a/vlib/x/json2/tests/any_test.v b/vlib/x/json2/tests/any_test.v index 9eb51569ab99ce..4ae9d3534969b1 100644 --- a/vlib/x/json2/tests/any_test.v +++ b/vlib/x/json2/tests/any_test.v @@ -118,11 +118,11 @@ fn test_bool() { fn test_str() { assert sample_data['int'] or { 0 }.str() == '1' assert sample_data['i64'] or { 0 }.str() == '128' - assert sample_data['f32'] or { 0 }.str() == '2.0' + assert sample_data['f32'] or { 0 }.str() == '2' assert sample_data['f64'] or { 0 }.str() == '1.283' assert sample_data['bool'] or { 0 }.str() == 'false' assert sample_data['str'] or { 0 }.str() == 'test' assert sample_data['null'] or { 0 }.str() == 'null' assert sample_data['arr'] or { 'not lol' }.str() == '["lol"]' - assert sample_data.str() == '{"int":1,"i64":128,"f32":2.0,"f64":1.283,"bool":false,"str":"test","null":null,"arr":["lol"],"obj":{"foo":10}}' + assert sample_data.str() == '{"int":1,"i64":128,"f32":2,"f64":1.283,"bool":false,"str":"test","null":null,"arr":["lol"],"obj":{"foo":10}}' } diff --git a/vlib/x/json2/tests/encode_struct_skippable_fields_test.v b/vlib/x/json2/tests/encode_struct_skippable_fields_test.v index 271e16fc019637..caa452c8233938 100644 --- a/vlib/x/json2/tests/encode_struct_skippable_fields_test.v +++ b/vlib/x/json2/tests/encode_struct_skippable_fields_test.v @@ -108,19 +108,19 @@ fn test_encode_struct_skipped_fields() { val1: 1 val2: 1.0 val3: 'string_val' - }) == '{"val1":1,"val2":1.0}' + }) == '{"val1":1,"val2":1}' assert json.encode(StructTypeSkippedFields4{ val: 'string_val' val1: 1 val2: 1.0 - }) == '{"val1":1,"val2":1.0,"val3":"0000-00-00T00:00:00.000Z"}' + }) == '{"val1":1,"val2":1,"val3":"0000-00-00T00:00:00.000Z"}' assert json.encode(StructTypeSkippedFields5{ val: 'string_val' val1: 1 val2: 1.0 - }) == '{"val2":1.0,"val3":"0000-00-00T00:00:00.000Z"}' + }) == '{"val2":1,"val3":"0000-00-00T00:00:00.000Z"}' assert json.encode(StructTypeSkippedFields6{ val: 'string_val' @@ -138,11 +138,11 @@ fn test_encode_struct_skipped_fields() { val: 'string_val' val1: 1 val2: 1.0 - }) == '{"val":"string_val","val2":1.0}' + }) == '{"val":"string_val","val2":1}' assert json.encode(StructTypeSkippedFields9{ val: 'string_val' val1: 1 val2: 1.0 - }) == '{"val":"string_val","val2":1.0}' + }) == '{"val":"string_val","val2":1}' } diff --git a/vlib/x/json2/tests/encode_struct_test.v b/vlib/x/json2/tests/encode_struct_test.v index 82a2fde7abb4f2..781e97781be731 100644 --- a/vlib/x/json2/tests/encode_struct_test.v +++ b/vlib/x/json2/tests/encode_struct_test.v @@ -133,9 +133,9 @@ fn test_array() { assert json.encode(StructType[[]f64]{}) == '{"val":[]}' assert json.encode(StructType[[]f64]{ val: [] }) == '{"val":[]}' - assert json.encode(StructType[[]f64]{ val: [f64(0)] }) == '{"val":[0.0]}' - assert json.encode(StructType[[]f64]{ val: [f64(1)] }) == '{"val":[1.0]}' - assert json.encode(StructType[[]f64]{ val: [f64(0), 1, 0, 2, 3, 2, 5, 1] }) == '{"val":[0.0,1.0,0.0,2.0,3.0,2.0,5.0,1.0]}' + assert json.encode(StructType[[]f64]{ val: [f64(0)] }) == '{"val":[0]}' + assert json.encode(StructType[[]f64]{ val: [f64(1)] }) == '{"val":[1]}' + assert json.encode(StructType[[]f64]{ val: [f64(0), 1, 0, 2, 3, 2, 5, 1] }) == '{"val":[0,1,0,2,3,2,5,1]}' assert json.encode(StructType[[]bool]{}) == '{"val":[]}' assert json.encode(StructType[[]bool]{ val: [] }) == '{"val":[]}' @@ -188,9 +188,9 @@ fn test_option_array() { assert json.encode(StructTypeOption[[]f64]{}) == '{}' assert json.encode(StructTypeOption[[]f64]{ val: [] }) == '{"val":[]}' - assert json.encode(StructTypeOption[[]f64]{ val: [f64(0)] }) == '{"val":[0.0]}' - assert json.encode(StructTypeOption[[]f64]{ val: [f64(1)] }) == '{"val":[1.0]}' - assert json.encode(StructTypeOption[[]f64]{ val: [f64(0), 1, 0, 2, 3, 2, 5, 1] }) == '{"val":[0.0,1.0,0.0,2.0,3.0,2.0,5.0,1.0]}' + assert json.encode(StructTypeOption[[]f64]{ val: [f64(0)] }) == '{"val":[0]}' + assert json.encode(StructTypeOption[[]f64]{ val: [f64(1)] }) == '{"val":[1]}' + assert json.encode(StructTypeOption[[]f64]{ val: [f64(0), 1, 0, 2, 3, 2, 5, 1] }) == '{"val":[0,1,0,2,3,2,5,1]}' assert json.encode(StructTypeOption[[]bool]{}) == '{}' assert json.encode(StructTypeOption[[]bool]{ val: [] }) == '{"val":[]}' diff --git a/vlib/x/json2/tests/json2_test.v b/vlib/x/json2/tests/json2_test.v index 97e1c7bb882ba5..f846b9d06f24fd 100644 --- a/vlib/x/json2/tests/json2_test.v +++ b/vlib/x/json2/tests/json2_test.v @@ -109,8 +109,8 @@ fn test_struct_with_number_to_map() { assert json.map_from(StructType[i16]{i16(-3)}).str() == '{"val":-3}' assert json.map_from(StructType[int]{-3}).str() == '{"val":-3}' assert json.map_from(StructType[i64]{-3}).str() == '{"val":-3}' - assert json.map_from(StructType[f32]{3.0}).str() == '{"val":3.0}' - assert json.map_from(StructType[f64]{3.0}).str() == '{"val":3.0}' + assert json.map_from(StructType[f32]{3.0}).str() == '{"val":3}' + assert json.map_from(StructType[f64]{3.0}).str() == '{"val":3}' assert json.map_from(StructType[u8]{3}).str() == '{"val":3}' assert json.map_from(StructType[u16]{3}).str() == '{"val":3}' assert json.map_from(StructType[u32]{3}).str() == '{"val":3}' diff --git a/vlib/x/json2/tests/json_module_compatibility_test/json_test.v b/vlib/x/json2/tests/json_module_compatibility_test/json_test.v index d95dfd2887e599..fdc6de2c53f17b 100644 --- a/vlib/x/json2/tests/json_module_compatibility_test/json_test.v +++ b/vlib/x/json2/tests/json_module_compatibility_test/json_test.v @@ -30,7 +30,7 @@ fn test_simple() { } x := Employee{'Peter', 28, 95000.5, .worker, sub_employee} s := json.encode[Employee](x) - assert s == '{"name":"Peter","age":28,"salary":95000.5,"title":2,"sub_employee":{"name":"João","age":0,"salary":0.0,"title":0}}' + assert s == '{"name":"Peter","age":28,"salary":95000.5,"title":2,"sub_employee":{"name":"João","age":0,"salary":0,"title":0}}' y := json.decode[Employee](s) or { println(err)