You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@[omitempty] in x.json2's encoder only handles a small set of primitive types (string, int, f32, f64, and the ? versions of those — see check_not_empty[T] in the encoder). It does not handle:
empty arrays ([]string{} etc.)
nil pointers (&Number = unsafe { nil })
empty maps
option struct (?SomeStruct) when none
The cJSON encoder skips all of those. The result is that ports of code from json produce noisy/incorrect output under x.json2.
V code
importx.json2structNumber {
min int
max int
}
pubstructResp {
pub:
options []string @[omitempty]
number &Number=unsafe { nil } @[omitempty]
}
fnmain() {
r:= Resp{ number: &Number{0, 0} }
println(json2.encode(r))
// expected: {"number":{"min":0,"max":0}}// actual: {"options":[],"number":{"min":0,"max":0}}
}
C backend result
// check_not_empty_T_Array_string is never specialised — the comptime// branches in the encoder only cover string/int/float/?string/?int/?f64/?f32.// As a result, the encoder writes "options":[] anyway.
Reproduction Steps
importx.json2structNumber {
min int
max int
}
pubstructResp {
pub:
options []string @[omitempty]
number &Number=unsafe { nil } @[omitempty]
}
fntest_omitempty_skips_empty_array_and_nil_pointer() {
r1:= Resp{ options: ['first', 'second'] }
r2:= Resp{ number: &Number{0, 0} }
assert json2.encode(r1) =='{"options":["first","second"]}'assert json2.encode(r2) =='{"number":{"min":0,"max":0}}'
}
> assert json2.encode(r2) == '{"number":{"min":0,"max":0}}'
Left value (len: 41): `{"options":[],"number":{"min":0,"max":0}}`
Right value (len: 28): `{"number":{"min":0,"max":0}}`
Possible Solution
Extend check_not_empty[T] in the encoder with comptime branches for:
$if T is $array: return val.len != 0
$if T is $map: return val.len != 0
$if T.indirections > 0: return !isnil(val)
$if T is $option: peek inside; for option struct return false when none, otherwise recurse.
$if T is $struct: arguably never empty — match cJSON's behaviour and treat as non-empty unless explicitly none.
Then in the field encoder loop, ensure the omitempty short-circuit honours the new return value before the field key is even written, including option-of-struct fields that currently bypass the check entirely.
Additional Information/Context
This is also the underlying reason the converted cJSON pointer-encode test fails when targeted at x.json2.
V version
V 0.5.1 1b3385cc34ff783e793d1a26a8ec5be587c80fe0.40b3711
Environment details (OS name and version, etc.)
|V full version |V 0.5.1 1b3385cc34ff783e793d1a26a8ec5be587c80fe0.40b3711
|:-------------------|:-------------------
|OS |linux, Ubuntu 24.04 LTS
|Processor |16 cpus, 64bit, little endian, AMD Ryzen 7 5800H with Radeon Graphics
|Memory |8.17GB/30.7GB
| |
|V executable |/home/hitalo/Documents/v/v
|V last modified time|2026-04-18 09:18:00
| |
|V home dir |OK, value: /home/hitalo/Documents/v
|VMODULES |OK, value: /home/hitalo/.vmodules
|VTMP |OK, value: /tmp/v_1000
|Current working dir |OK, value: /home/hitalo/Documents/v
| |
|Git version |git version 2.43.0
|V git status |0.5.1-1006-g40b3711b-dirty
|.git/config present |true
| |
|cc version |cc (GCC) 14.2.0
|gcc version |gcc (GCC) 14.2.0
|clang version |Ubuntu clang version 18.1.3 (1)
|tcc version |tcc version 0.9.28rc 2025-02-13 HEAD@f8bd136d (x86_64 Linux)
|tcc git status |thirdparty-linux-amd64 696c1d84
|emcc version |emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.6 ()
|glibc version |ldd (Ubuntu GLIBC 2.39-0ubuntu8.3) 2.39
Note
You can use the 👍 reaction to increase the issue's priority for developers.
Please note that only the 👍 reaction to the issue itself counts as a vote.
Other reactions and those to comments will not be taken into account.
Describe the bug
@[omitempty]inx.json2's encoder only handles a small set of primitive types (string,int,f32,f64, and the?versions of those — seecheck_not_empty[T]in the encoder). It does not handle:[]string{}etc.)&Number = unsafe { nil })?SomeStruct) whennoneThe cJSON encoder skips all of those. The result is that ports of code from
jsonproduce noisy/incorrect output underx.json2.V code
C backend result
Reproduction Steps
Expected Behavior
Current Behavior
Possible Solution
Extend
check_not_empty[T]in the encoder with comptime branches for:$if T is $array: returnval.len != 0$if T is $map: returnval.len != 0$if T.indirections > 0: return!isnil(val)$if T is $option: peek inside; for option struct returnfalsewhennone, otherwise recurse.$if T is $struct: arguably never empty — match cJSON's behaviour and treat as non-empty unless explicitlynone.Then in the field encoder loop, ensure the omitempty short-circuit honours the new return value before the field key is even written, including option-of-struct fields that currently bypass the check entirely.
Additional Information/Context
This is also the underlying reason the converted cJSON pointer-encode test fails when targeted at
x.json2.V version
Environment details (OS name and version, etc.)
Note
You can use the 👍 reaction to increase the issue's priority for developers.
Please note that only the 👍 reaction to the issue itself counts as a vote.
Other reactions and those to comments will not be taken into account.