Skip to content

Commit abb0cf7

Browse files
authored
x.json2: support sumtype encoding in a more robust way (#20093)
1 parent 110e9f1 commit abb0cf7

File tree

3 files changed

+59
-119
lines changed

3 files changed

+59
-119
lines changed

vlib/v/tests/bench/bench_json_vs_json2.v

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ fn benchmark_measure_encode_by_type() ! {
9393
measure_json_encode_old_vs_new(StructType[[]int]{})!
9494
measure_json_encode_old_vs_new(StructType[StructType[int]]{ val: StructType[int]{} })!
9595
measure_json_encode_old_vs_new(StructType[Enum]{})!
96+
measure_json_encode_old_vs_new(StructType[SumTypes]{1})!
9697
}
9798

9899
fn benchmark_measure_encode_by_alias_type() ! {

vlib/x/json2/encode_struct_test.v

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ type TimeAlias = time.Time
1818
type StructAlias = StructType[int]
1919
type EnumAlias = Enumerates
2020

21-
type SumTypes = StructType[string] | bool | int | string | time.Time
21+
type SumTypes = StructType[string] | []SumTypes | []string | bool | int | string | time.Time
2222

2323
enum Enumerates {
2424
a
@@ -150,6 +150,11 @@ fn test_array() {
150150
val: false
151151
}]
152152
assert json.encode(StructType[[]StructType[bool]]{ val: array_of_struct }) == '{"val":[{"val":true},{"val":false}]}'
153+
154+
assert json.encode(StructType[[][]string]{ val: [['1'], ['2']] }) == '{"val":[["1"],["2"]]}'
155+
156+
// error: cannot use `[][]string` as `[]string` in argument 1 to `x.json2.Encoder.encode_array`
157+
// assert json.encode(StructType[[][][]string]{ val: [[['1']]] }) == '{"val":[[["1"]]]}'
153158
}
154159

155160
fn test_option_array() {
@@ -258,23 +263,30 @@ fn test_sumtypes() {
258263
assert json.encode(StructType[SumTypes]{ val: 0 }) == '{"val":0}'
259264
assert json.encode(StructType[SumTypes]{ val: 1 }) == '{"val":1}'
260265

261-
assert json.encode(StructType[SumTypes]{ val: fixed_time }) == '{"val":2022-03-11T13:54:25.000Z}'
266+
assert json.encode(StructType[SumTypes]{ val: fixed_time }) == '{"val":"2022-03-11T13:54:25.000Z"}'
262267

263268
assert json.encode(StructType[StructType[SumTypes]]{
264269
val: StructType[SumTypes]{
265270
val: 1
266271
}
267272
}) == '{"val":{"val":1}}'
268273

269-
// assert json.encode(StructType[SumTypes]{ val: StructType[string]{
270-
// val: '111111'
271-
// } }) == '{"val":1}'
274+
assert json.encode(StructType[SumTypes]{
275+
val: StructType[string]{
276+
val: '111111'
277+
}
278+
}) == '{"val":{"val":"111111"}}'
272279

273280
assert json.encode(StructType[StructType[SumTypes]]{
274281
val: StructType[SumTypes]{
275282
val: 1
276283
}
277284
}) == '{"val":{"val":1}}'
285+
286+
// assert json.encode(StructType{ val: [SumTypes('a')] }) == '{"val":["a"]}'
287+
// assert json.encode(StructType[SumTypes]{ val: ['a'] }) == '{"val":["a"]}'
288+
// assert json.encode(StructType[SumTypes]{ val: [SumTypes('a')] }) == '{"val":["a"]}'
289+
// assert json.encode(StructType[SumTypes]{ val: '' }) == '{"val":""}'
278290
}
279291

280292
fn test_maps() {

vlib/x/json2/encoder.v

Lines changed: 41 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,7 @@ fn (e &Encoder) encode_any(val Any, level int, mut buf []u8) ! {
119119
buf << `"`
120120
}
121121
Null {
122-
buf << `n`
123-
buf << `u`
124-
buf << `l`
125-
buf << `l`
122+
unsafe { buf.push_many(json2.null_in_bytes.str, json2.null_in_bytes.len) }
126123
}
127124
}
128125
}
@@ -132,7 +129,8 @@ fn (e &Encoder) encode_map[T](value T, level int, mut buf []u8) ! {
132129
mut idx := 0
133130
for k, v in value {
134131
e.encode_newline(level, mut buf)!
135-
e.encode_string(k.str(), mut buf)!
132+
// e.encode_string(k.str(), mut buf)!
133+
e.encode_string(k, mut buf)!
136134
buf << json2.colon_rune
137135
if e.newline != 0 {
138136
buf << ` `
@@ -143,7 +141,8 @@ fn (e &Encoder) encode_map[T](value T, level int, mut buf []u8) ! {
143141
}
144142
idx++
145143
}
146-
e.encode_newline(level, mut buf)!
144+
// e.encode_newline(level, mut buf)!
145+
e.encode_newline(level - 1, mut buf)!
147146
buf << json2.curly_close_rune
148147
}
149148

@@ -152,30 +151,38 @@ fn (e &Encoder) encode_value_with_level[T](val T, level int, mut buf []u8) ! {
152151
e.encode_string(val, mut buf)!
153152
} $else $if T is Any {
154153
e.encode_any(val, level, mut buf)!
155-
} $else $if T is map[string]Any {
156-
// weird quirk but val is destructured immediately to Any
157-
e.encode_any(val, level, mut buf)!
154+
} $else $if T is $sumtype {
155+
if val.str() != 'unknown sum type value' {
156+
$for v in val.variants {
157+
if val is v {
158+
e.encode_value_with_level(val, level, mut buf)!
159+
}
160+
}
161+
}
162+
} $else $if T is $alias {
163+
// TODO
164+
} $else $if T is time.Time {
165+
parsed_time := time.parse(val.str()) or { time.Time{} }
166+
e.encode_string(parsed_time.format_rfc3339(), mut buf)!
158167
} $else $if T is $map {
159168
e.encode_map(val, level, mut buf)!
160169
} $else $if T is []Any {
170+
// TODO test
161171
e.encode_any(val, level, mut buf)!
162172
} $else $if T is Encodable {
163173
str_value := val.json_str()
164174
unsafe { buf.push_many(str_value.str, str_value.len) }
165175
} $else $if T is $struct {
166176
e.encode_struct(val, level, mut buf)!
167177
} $else $if T is $enum {
168-
e.encode_any(Any(int(val)), level, mut buf)!
169-
} $else $if T is bool {
170-
e.encode_any(val, level, mut buf)!
171-
} $else $if T is $float {
172-
e.encode_any(val, level, mut buf)!
173-
} $else $if T is $int {
174-
e.encode_any(val, level, mut buf)!
178+
str_int := int(val).str()
179+
unsafe { buf.push_many(str_int.str, str_int.len) }
180+
} $else $if T is $int || T is $float || T is bool {
181+
str_int := val.str()
182+
unsafe { buf.push_many(str_int.str, str_int.len) }
175183
} $else $if T is Null {
176-
e.encode_any(val, level, mut buf)!
184+
unsafe { buf.push_many(json2.null_in_bytes.str, json2.null_in_bytes.len) }
177185
} $else {
178-
// dump(val.str())
179186
return error('cannot encode value with ${typeof(val).name} type')
180187
}
181188
}
@@ -333,72 +340,18 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut buf []u8) ! {
333340
e.encode_map(value, level + 1, mut buf)!
334341
} $else $if field.is_enum {
335342
// TODO - replace for `field.typ is $enum`
336-
str_value := int(val.$(field.name)).str()
337-
unsafe { buf.push_many(str_value.str, str_value.len) }
343+
// str_value := int(val.$(field.name)).str()
344+
// unsafe { buf.push_many(str_value.str, str_value.len) }
345+
e.encode_value_with_level(val.$(field.name), level + 1, mut buf)!
338346
} $else $if field.typ is $enum {
339-
// wr.write(int(val.$(field.name)).str().bytes())! // FIXME - error: cannot cast string to `int`, use `val.$field.name.int()` instead.
340347
} $else $if field.typ is $sumtype {
341-
// dump(val.$(field.name).str())
342-
// dump(is_none)
343-
sum_type_value := value.str()#[typeof(val.$(field.name)).name.len + 1..-1]
344-
345-
is_string := sum_type_value[0] == "'"[0]
346-
347-
// mut is_struct := false
348-
// mut is_sumtype := false
349-
// mut is_enum := false
350-
// mut is_array := false
351-
352-
match sum_type_value[0] {
353-
`0`...`9` {
354-
if sum_type_value.contains_any(' /:-') {
355-
date_time_str := time.parse(sum_type_value)!
356-
unsafe {
357-
str_value := date_time_str.format_rfc3339()
358-
buf.push_many(str_value.str, str_value.len)
359-
}
360-
} else {
361-
unsafe { buf.push_many(sum_type_value.str, sum_type_value.len) }
348+
field_value := val.$(field.name)
349+
if field_value.str() != 'unknown sum type value' {
350+
$for v in field_value.variants {
351+
if field_value is v {
352+
e.encode_value_with_level(field_value, level, mut buf)!
362353
}
363354
}
364-
`A`...`Z` {
365-
// SumTypes(0)
366-
if sum_type_value.contains('(') {
367-
if !sum_type_value.all_before('(').contains_any(' "\'[') {
368-
// is_sumtype = true
369-
}
370-
}
371-
// StructType{
372-
// StructType[int]{
373-
if sum_type_value.contains('{') {
374-
if !sum_type_value.all_before('{').contains_any(' "\'') {
375-
// is_struct = true
376-
// TODO
377-
// e.encode_struct_from_sumtype(value, level + 1, mut buf)!
378-
}
379-
}
380-
}
381-
`a`...`z` {
382-
if sum_type_value in ['true', 'false'] {
383-
unsafe { buf.push_many(sum_type_value.str, sum_type_value.len) }
384-
} else {
385-
// is_enum = true
386-
}
387-
}
388-
else {
389-
// dump('else')
390-
}
391-
}
392-
// dump(sum_type_value)
393-
394-
// dump(is_none)
395-
// dump(is_string)
396-
// dump(is_struct)
397-
// dump(is_sumtype)
398-
// dump(is_enum)
399-
// dump(is_array)
400-
if is_string {
401-
e.encode_string(sum_type_value#[1..-1], mut buf)!
402355
}
403356
} $else $if field.typ is $alias {
404357
$if field.unaliased_typ is string {
@@ -416,18 +369,13 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut buf []u8) ! {
416369
str_value := val.$(field.name).str()
417370
unsafe { buf.push_many(str_value.str, str_value.len) }
418371
} $else $if field.unaliased_typ is $array {
419-
// e.encode_array(val.$(field.name), level + 1, mut buf)! // FIXME - error: could not infer generic type `U` in call to `encode_array`
372+
// TODO
420373
} $else $if field.unaliased_typ is $struct {
421-
// e.encode_struct(val.$(field.name), level + 1, mut buf)! // FIXME - error: cannot use `BoolAlias` as `StringAlias` in argument 1 to `x.json2.Encoder.encode_struct`
422374
e.encode_struct(value, level + 1, mut buf)!
423375
} $else $if field.unaliased_typ is $enum {
424-
// enum_value := val.$(field.name)
425-
// dump(int(val.$(field.name))) // FIXME
426-
// dump(val.$(field.name).int()) // FIXME - error: unknown method or field: `BoolAlias.int`
427-
// dump(val.$(field.name).int()) // FIXME - error: cannot convert 'enum <anonymous>' to 'struct string'
428-
429-
// wr.write(val.$(field.name).int().str().bytes())! // FIXME - error: unknown method or field: `BoolAlias.int`
376+
// TODO
430377
} $else $if field.unaliased_typ is $sumtype {
378+
// TODO
431379
} $else {
432380
return error('the alias ${typeof(val).name} cannot be encoded')
433381
}
@@ -455,42 +403,21 @@ fn (e &Encoder) encode_array[U](val []U, level int, mut buf []u8) ! {
455403
for i in 0 .. val.len {
456404
e.encode_newline(level, mut buf)!
457405

458-
$if U is string {
459-
e.encode_any(val[i], level + 1, mut buf)!
460-
} $else $if U is bool {
461-
e.encode_any(bool(val[i]), level + 1, mut buf)!
462-
} $else $if U is f32 {
463-
e.encode_any(f32(val[i]), level + 1, mut buf)!
464-
} $else $if U is f64 {
465-
e.encode_any(f64(val[i]), level + 1, mut buf)!
466-
} $else $if U is i8 {
467-
e.encode_any(i8(val[i]), level + 1, mut buf)!
468-
} $else $if U is i16 {
469-
e.encode_any(i16(val[i]), level + 1, mut buf)!
470-
} $else $if U is int {
471-
e.encode_any(int(val[i]), level + 1, mut buf)!
472-
} $else $if U is i64 {
473-
e.encode_any(i64(val[i]), level + 1, mut buf)!
474-
} $else $if U is u8 {
475-
e.encode_any(u8(val[i]), level + 1, mut buf)!
476-
} $else $if U is u16 {
477-
e.encode_any(u16(val[i]), level + 1, mut buf)!
478-
} $else $if U is u32 {
479-
e.encode_any(u32(val[i]), level + 1, mut buf)!
480-
} $else $if U is u64 {
481-
e.encode_any(u64(val[i]), level + 1, mut buf)!
406+
$if U is string || U is bool || U is $int || U is $float {
407+
e.encode_value_with_level(val[i], level + 1, mut buf)!
482408
} $else $if U is $array {
483-
// FIXME - error: could not infer generic type `U` in call to `encode_array`
484-
// e.encode_array(val[i], level + 1, mut buf)!
409+
e.encode_array(val[i], level + 1, mut buf)!
485410
} $else $if U is $struct {
486411
e.encode_struct(val[i], level + 1, mut buf)!
487412
} $else $if U is $sumtype {
413+
// TODO test
488414
$if U is Any {
489415
e.encode_any(val[i], level + 1, mut buf)!
490416
} $else {
491417
// TODO
492418
}
493419
} $else $if U is $enum {
420+
// TODO test
494421
e.encode_any(i64(val[i]), level + 1, mut buf)!
495422
} $else {
496423
return error('type ${typeof(val).name} cannot be array encoded')

0 commit comments

Comments
 (0)