Skip to content

Commit

Permalink
x.json2: make encode number cJson compatible (fix #22363) (#22419)
Browse files Browse the repository at this point in the history
  • Loading branch information
kbkpbot authored Oct 6, 2024
1 parent 3c0358c commit e83b0d2
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 19 deletions.
30 changes: 27 additions & 3 deletions vlib/x/json2/encoder.v
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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')
}
Expand Down Expand Up @@ -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)!
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
}
4 changes: 2 additions & 2 deletions vlib/x/json2/tests/any_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -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}}'
}
10 changes: 5 additions & 5 deletions vlib/x/json2/tests/encode_struct_skippable_fields_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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}'
}
12 changes: 6 additions & 6 deletions vlib/x/json2/tests/encode_struct_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -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":[]}'
Expand Down Expand Up @@ -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":[]}'
Expand Down
4 changes: 2 additions & 2 deletions vlib/x/json2/tests/json2_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -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}'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit e83b0d2

Please sign in to comment.