Skip to content

Commit 8747448

Browse files
robbat2codex
andcommitted
feat: JSON5 support
Reference: https://spec.json5.org/ Co-authored-by: Codex <codex@openai.com> Generated-with: OpenAI Codex CLI (partial) Signed-off-by: Robin H. Johnson <rjohnson@coreweave.com>
1 parent f98028c commit 8747448

17 files changed

Lines changed: 1428 additions & 4 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ Flags:
415415
-h, --help help for yq
416416
-I, --indent int sets indent level for output (default 2)
417417
-i, --inplace update the file in place of first file given.
418-
-p, --input-format string [auto|a|yaml|y|json|j|kyaml|ky|props|p|csv|c|tsv|t|xml|x|base64|uri|toml|hcl|h|lua|l|ini|i] parse format for input. (default "auto")
418+
-p, --input-format string [auto|a|yaml|y|json|j|json5|j5|kyaml|ky|props|p|csv|c|tsv|t|xml|x|base64|uri|toml|hcl|h|lua|l|ini|i] parse format for input. (default "auto")
419419
--lua-globals output keys as top-level global variables
420420
--lua-prefix string prefix (default "return ")
421421
--lua-suffix string suffix (default ";\n")
@@ -424,7 +424,7 @@ Flags:
424424
-N, --no-doc Don't print document separators (---)
425425
-0, --nul-output Use NUL char to separate values. If unwrap scalar is also set, fail if unwrapped scalar contains NUL char.
426426
-n, --null-input Don't read input, simply evaluate the expression given. Useful for creating docs from scratch.
427-
-o, --output-format string [auto|a|yaml|y|json|j|kyaml|ky|props|p|csv|c|tsv|t|xml|x|base64|uri|toml|hcl|h|shell|s|lua|l|ini|i] output format type. (default "auto")
427+
-o, --output-format string [auto|a|yaml|y|json|j|json5|j5|kyaml|ky|props|p|csv|c|tsv|t|xml|x|base64|uri|toml|hcl|h|shell|s|lua|l|ini|i] output format type. (default "auto")
428428
-P, --prettyPrint pretty print, shorthand for '... style = ""'
429429
--properties-array-brackets use [x] in array paths (e.g. for SpringBoot)
430430
--properties-separator string separator to use between keys and values (default " = ")

acceptance_tests/inputs-format-auto.sh

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ setUp() {
55
rm test*.toml 2>/dev/null || true
66
rm test*.tfstate 2>/dev/null || true
77
rm test*.json 2>/dev/null || true
8+
rm test*.json5 2>/dev/null || true
89
rm test*.properties 2>/dev/null || true
910
rm test*.csv 2>/dev/null || true
1011
rm test*.tsv 2>/dev/null || true
@@ -31,6 +32,29 @@ EOM
3132
assertEquals "$expected" "$X"
3233
}
3334

35+
testInputJson5() {
36+
cat >test.json5 <<'EOL'
37+
{ /* hello */
38+
mike: { things: "cool", },
39+
}
40+
EOL
41+
42+
read -r -d '' expected << EOM
43+
// hello
44+
{
45+
"mike": {
46+
"things": "cool"
47+
}
48+
}
49+
EOM
50+
51+
X=$(./yq test.json5)
52+
assertEquals "$expected" "$X"
53+
54+
X=$(./yq ea test.json5)
55+
assertEquals "$expected" "$X"
56+
}
57+
3458
testInputToml() {
3559
cat >test.toml <<EOL
3660
[owner]
@@ -84,6 +108,27 @@ EOM
84108
assertEquals "$expected" "$X"
85109
}
86110

111+
testInputJson5OutputYaml() {
112+
cat >test.json5 <<'EOL'
113+
{
114+
// comment
115+
mike: { things: "cool", },
116+
}
117+
EOL
118+
119+
read -r -d '' expected << EOM
120+
# comment
121+
mike:
122+
things: cool
123+
EOM
124+
125+
X=$(./yq test.json5 -oy)
126+
assertEquals "$expected" "$X"
127+
128+
X=$(./yq ea test.json5 -oy)
129+
assertEquals "$expected" "$X"
130+
}
131+
87132
testInputProperties() {
88133
cat >test.properties <<EOL
89134
mike.things = hello

acceptance_tests/inputs-format.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ setUp() {
77
rm test*.tsv 2>/dev/null || true
88
rm test*.xml 2>/dev/null || true
99
rm test*.tf 2>/dev/null || true
10+
rm test*.json5 2>/dev/null || true
11+
rm test*.kyaml 2>/dev/null || true
1012
}
1113

1214
testInputProperties() {
@@ -185,6 +187,40 @@ EOM
185187
assertEquals "$expected" "$X"
186188
}
187189

190+
testInputJson5MultilineBlockComments() {
191+
cat >test.json5 <<'EOL'
192+
{
193+
/*
194+
multiline
195+
block comment
196+
*/
197+
first: 1,
198+
second/* inline block */: 2,
199+
third: /* before value */ 3,
200+
fourth: [1, /* between elements */ 2,],
201+
}
202+
EOL
203+
204+
read -r -d '' expected <<'EOM'
205+
# multiline
206+
# block comment
207+
first: 1
208+
second: 2 # inline block
209+
third: 3
210+
# before value
211+
fourth:
212+
- 1
213+
# between elements
214+
- 2
215+
EOM
216+
217+
X=$(./yq e -p=json5 test.json5)
218+
assertEquals "$expected" "$X"
219+
220+
X=$(./yq ea -p=json5 test.json5)
221+
assertEquals "$expected" "$X"
222+
}
223+
188224

189225

190226

acceptance_tests/output-format.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,28 @@ EOM
4848
assertEquals "$expected" "$X"
4949
}
5050

51+
testOutputJson5() {
52+
cat >test.yml <<EOL
53+
a: {b: ["cat"]}
54+
EOL
55+
56+
read -r -d '' expected << EOM
57+
{
58+
"a": {
59+
"b": [
60+
"cat"
61+
]
62+
}
63+
}
64+
EOM
65+
66+
X=$(./yq e --output-format=json5 test.yml)
67+
assertEquals "$expected" "$X"
68+
69+
X=$(./yq ea --output-format=json5 test.yml)
70+
assertEquals "$expected" "$X"
71+
}
72+
5173
testOutputYamlRawDefault() {
5274
cat >test.yml <<EOL
5375
a: "cat"

cmd/utils_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,13 @@ func TestConfigureDecoder(t *testing.T) {
220220
expectError: false,
221221
expectType: "jsonDecoder",
222222
},
223+
{
224+
name: "json5 format",
225+
inputFormat: "json5",
226+
evaluateTogether: true,
227+
expectError: false,
228+
expectType: "json5Decoder",
229+
},
223230
{
224231
name: "xml format",
225232
inputFormat: "xml",
@@ -390,6 +397,12 @@ func TestConfigureEncoder(t *testing.T) {
390397
expectError: false,
391398
expectType: "jsonEncoder",
392399
},
400+
{
401+
name: "json5 format",
402+
outputFormat: "json5",
403+
expectError: false,
404+
expectType: "json5Encoder",
405+
},
393406
{
394407
name: "xml format",
395408
outputFormat: "xml",
@@ -1265,6 +1278,14 @@ func TestConfigureInputFormat(t *testing.T) {
12651278
expectInput: "json",
12661279
expectOutput: "json",
12671280
},
1281+
{
1282+
name: "auto format with json5 file",
1283+
inputFilename: "file.json5",
1284+
inputFormat: "auto",
1285+
outputFormat: "auto",
1286+
expectInput: "json5",
1287+
expectOutput: "json5",
1288+
},
12681289
{
12691290
name: "auto format with unknown file",
12701291
inputFilename: "file.unknown",

examples/multiline-comments.json5

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
/*
3+
multiline block comment
4+
spanning multiple lines
5+
*/
6+
first: 1,
7+
second/* inline block comment */: 2,
8+
third: /* before value */ 3,
9+
fourth: [
10+
1,
11+
/* between elements */
12+
2,
13+
],
14+
}
15+

examples/sample.json5

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// JSON5 example file for yq
2+
{
3+
// comments
4+
unquotedKey: 'single quoted string',
5+
trailingCommaArray: [1, 2,],
6+
7+
// numbers
8+
hexInt: 0x10,
9+
posInf: Infinity,
10+
negInf: -Infinity,
11+
notANumber: NaN,
12+
13+
nested: {
14+
a: 1,
15+
b: 2,
16+
},
17+
}
18+

0 commit comments

Comments
 (0)