From 6b0743bb07f8d003ff703dc48ae2120cd8bc152a Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sat, 19 Nov 2022 11:52:17 +0200 Subject: [PATCH] x.json2: add support for i8, i16, u8, u16, u32 (fix #16484) --- vlib/x/json2/any_test.v | 8 +-- vlib/x/json2/decoder.v | 37 ------------- vlib/x/json2/encoder.v | 11 ++-- vlib/x/json2/integer_primitives_test.v | 53 +++++++++++++++++++ vlib/x/json2/json2.v | 73 +++++++++++--------------- vlib/x/json2/types.v | 56 ++++++++++++++++++++ 6 files changed, 152 insertions(+), 86 deletions(-) create mode 100644 vlib/x/json2/integer_primitives_test.v create mode 100644 vlib/x/json2/types.v diff --git a/vlib/x/json2/any_test.v b/vlib/x/json2/any_test.v index e7731fbed2..39123e8d56 100644 --- a/vlib/x/json2/any_test.v +++ b/vlib/x/json2/any_test.v @@ -108,10 +108,10 @@ fn test_bool() { assert sample_data['bool'] or { 0 }.bool() == false assert json2.Any('true').bool() == true // invalid conversions - assert sample_data['int'] or { 0 }.bool() == false - assert sample_data['i64'] or { 0 }.bool() == false - assert sample_data['f32'] or { 0 }.bool() == false - assert sample_data['f64'] or { 0 }.bool() == false + assert sample_data['int'] or { 0 }.bool() == true + assert sample_data['i64'] or { 0 }.bool() == true + assert sample_data['f32'] or { 0 }.bool() == true + assert sample_data['f64'] or { 0 }.bool() == true assert sample_data['null'] or { 0 }.bool() == false assert sample_data['arr'] or { 0 }.bool() == false assert sample_data['obj'] or { 0 }.bool() == false diff --git a/vlib/x/json2/decoder.v b/vlib/x/json2/decoder.v index afa307c9f7..226c6d87ef 100644 --- a/vlib/x/json2/decoder.v +++ b/vlib/x/json2/decoder.v @@ -3,43 +3,6 @@ // that can be found in the LICENSE file. module json2 -// `Any` is a sum type that lists the possible types to be decoded and used. -pub type Any = Null - | []Any - | []int - | bool - | f32 - | f64 - | i64 - | int - | map[string]Any - | string - | u64 - -// `Null` struct is a simple representation of the `null` value in JSON. -pub struct Null { - is_null bool = true -} - -pub enum ValueKind { - unknown - array - object - string_ - number -} - -// str returns the string representation of the specific ValueKind -pub fn (k ValueKind) str() string { - return match k { - .unknown { 'unknown' } - .array { 'array' } - .object { 'object' } - .string_ { 'string' } - .number { 'number' } - } -} - fn format_message(msg string, line int, column int) string { return '[x.json2] ${msg} (${line}:${column})' } diff --git a/vlib/x/json2/encoder.v b/vlib/x/json2/encoder.v index d9481a56cc..9d401374d1 100644 --- a/vlib/x/json2/encoder.v +++ b/vlib/x/json2/encoder.v @@ -16,8 +16,7 @@ pub struct Encoder { pub const default_encoder = Encoder{} -// byte array versions of the most common tokens/chars -// to avoid reallocations +// byte array versions of the most common tokens/chars to avoid reallocations const null_in_bytes = 'null'.bytes() const true_in_bytes = 'true'.bytes() @@ -65,7 +64,10 @@ fn (e &Encoder) encode_any(val Any, level int, mut wr io.Writer) ! { wr.write(json2.false_in_bytes)! } } - int, u64, i64 { + i8, i16, int, i64 { + wr.write(val.str().bytes())! + } + u8, u16, u32, u64 { wr.write(val.str().bytes())! } f32, f64 { @@ -140,7 +142,8 @@ fn (e &Encoder) encode_value_with_level(val T, level int, mut wr io.Writer) ! e.encode_any(val, level, mut wr)! } $else $if T is []Any { e.encode_any(val, level, mut wr)! - } $else $if T is Null || T is bool || T is f32 || T is f64 || T is i64 || T is int || T is u64 { + } $else $if T is Null || T is bool || T is f32 || T is f64 || T is i8 || T is i16 || T is int + || T is i64 || T is u8 || T is u16 || T is u32 || T is u64 { e.encode_any(val, level, mut wr)! } $else $if T is Encodable { wr.write(val.json_str().bytes())! diff --git a/vlib/x/json2/integer_primitives_test.v b/vlib/x/json2/integer_primitives_test.v new file mode 100644 index 0000000000..7ddcdc642e --- /dev/null +++ b/vlib/x/json2/integer_primitives_test.v @@ -0,0 +1,53 @@ +import x.json2 + +struct IntegerValues { +mut: + ux8 u8 + ux16 u16 + ux32 u32 + ux64 u64 + sx8 i8 + sx16 i16 + sx32 int + sx64 i64 +} + +pub fn (mut x IntegerValues) from_json(f json2.Any) { + fm := f.as_map() + if v := fm['ux8'] { + x.ux8 = u8(v.u64()) + } + if v := fm['ux16'] { + x.ux16 = u16(v.u64()) + } + if v := fm['ux32'] { + x.ux32 = u32(v.u64()) + } + if v := fm['ux64'] { + x.ux64 = v.u64() + } + // + if v := fm['sx8'] { + x.sx8 = i8(v.i64()) + } + if v := fm['sx16'] { + x.sx16 = i16(v.i64()) + } + if v := fm['sx32'] { + x.sx32 = int(v.i64()) + } + if v := fm['sx64'] { + x.sx64 = v.i64() + } +} + +fn test_all_primitive_integer_types_are_encodable_and_decodable() { + f := IntegerValues{1, 2, 3, 4, -1, -2, -3, -4} + s := json2.encode(f) + dump(s) + assert s == '{"ux8":1,"ux16":2,"ux32":3,"ux64":4,"sx8":-1,"sx16":-2,"sx32":-3,"sx64":-4}' + x := json2.decode(s)! + dump(x) + assert x == f + println('done') +} diff --git a/vlib/x/json2/json2.v b/vlib/x/json2/json2.v index e9112ad471..cb2120a3e7 100644 --- a/vlib/x/json2/json2.v +++ b/vlib/x/json2/json2.v @@ -5,18 +5,6 @@ module json2 import strings -pub const ( - null = Null{} -) - -pub interface Decodable { - from_json(f Any) -} - -pub interface Encodable { - json_str() string -} - // Decodes a JSON string into an `Any` type. Returns an option. pub fn raw_decode(src string) !Any { mut p := new_parser(src, true) @@ -45,32 +33,16 @@ pub fn encode(val T) string { } default_encoder.encode_value(val, mut sb) or { dump(err) - default_encoder.encode_value(json2.null, mut sb) or {} + default_encoder.encode_value(null, mut sb) or {} } return sb.str() } -// as_map uses `Any` as a map. -pub fn (f Any) as_map() map[string]Any { - if f is map[string]Any { - return f - } else if f is []Any { - mut mp := map[string]Any{} - for i, fi in f { - mp['${i}'] = fi - } - return mp - } - return { - '0': f - } -} - // int uses `Any` as an integer. pub fn (f Any) int() int { match f { int { return f } - i64, f32, f64, bool { return int(f) } + i8, i16, i64, u8, u16, u32, u64, f32, f64, bool { return int(f) } else { return 0 } } } @@ -79,7 +51,7 @@ pub fn (f Any) int() int { pub fn (f Any) i64() i64 { match f { i64 { return f } - int, f32, f64, bool { return i64(f) } + i8, i16, int, u8, u16, u32, u64, f32, f64, bool { return i64(f) } else { return 0 } } } @@ -88,7 +60,7 @@ pub fn (f Any) i64() i64 { pub fn (f Any) u64() u64 { match f { u64 { return f } - int, i64, f32, f64, bool { return u64(f) } + u8, u16, u32, i8, i16, int, i64, f32, f64, bool { return u64(f) } else { return 0 } } } @@ -97,20 +69,32 @@ pub fn (f Any) u64() u64 { pub fn (f Any) f32() f32 { match f { f32 { return f } - int, i64, f64 { return f32(f) } + bool, i8, i16, int, i64, u8, u16, u32, u64, f64 { return f32(f) } else { return 0.0 } } } -// f64 uses `Any` as a float. +// f64 uses `Any` as a 64-bit float. pub fn (f Any) f64() f64 { match f { f64 { return f } - int, i64, f32 { return f64(f) } + i8, i16, int, i64, u8, u16, u32, u64, f32 { return f64(f) } else { return 0.0 } } } +// bool uses `Any` as a bool. +pub fn (f Any) bool() bool { + match f { + bool { return f } + string { return f.bool() } + i8, i16, int, i64 { return i64(f) != 0 } + u8, u16, u32, u64 { return u64(f) != 0 } + f32, f64 { return f64(f) != 0.0 } + else { return false } + } +} + // arr uses `Any` as an array. pub fn (f Any) arr() []Any { if f is []Any { @@ -125,11 +109,18 @@ pub fn (f Any) arr() []Any { return [f] } -// bool uses `Any` as a bool -pub fn (f Any) bool() bool { - match f { - bool { return f } - string { return f.bool() } - else { return false } +// as_map uses `Any` as a map. +pub fn (f Any) as_map() map[string]Any { + if f is map[string]Any { + return f + } else if f is []Any { + mut mp := map[string]Any{} + for i, fi in f { + mp['${i}'] = fi + } + return mp + } + return { + '0': f } } diff --git a/vlib/x/json2/types.v b/vlib/x/json2/types.v new file mode 100644 index 0000000000..272e698761 --- /dev/null +++ b/vlib/x/json2/types.v @@ -0,0 +1,56 @@ +module json2 + +// `Any` is a sum type that lists the possible types to be decoded and used. +pub type Any = Null + | []Any + | []int + | bool + | f32 + | f64 + | i16 + | i64 + | i8 + | int + | map[string]Any + | string + | u16 + | u32 + | u64 + | u8 + +// Decodable is an interface, that allows custom implementations for decoding structs from JSON encoded values +pub interface Decodable { + from_json(f Any) +} + +// Decodable is an interface, that allows custom implementations for encoding structs to their string based JSON representations +pub interface Encodable { + json_str() string +} + +// `Null` struct is a simple representation of the `null` value in JSON. +pub struct Null { + is_null bool = true +} + +pub const null = Null{} + +// ValueKind enumerates the kinds of possible values of the Any sumtype. +pub enum ValueKind { + unknown + array + object + string_ + number +} + +// str returns the string representation of the specific ValueKind +pub fn (k ValueKind) str() string { + return match k { + .unknown { 'unknown' } + .array { 'array' } + .object { 'object' } + .string_ { 'string' } + .number { 'number' } + } +}