mirror of
https://github.com/lightbend/config.git
synced 2025-02-20 00:00:48 +08:00
allow omitting curly braces on root object
This commit is contained in:
parent
39de5aa444
commit
4051d07870
@ -265,7 +265,7 @@ final class Parser {
|
|||||||
if (Tokens.isValue(token)) {
|
if (Tokens.isValue(token)) {
|
||||||
return Tokens.getValue(token);
|
return Tokens.getValue(token);
|
||||||
} else if (token == Tokens.OPEN_CURLY) {
|
} else if (token == Tokens.OPEN_CURLY) {
|
||||||
return parseObject();
|
return parseObject(true);
|
||||||
} else if (token == Tokens.OPEN_SQUARE) {
|
} else if (token == Tokens.OPEN_SQUARE) {
|
||||||
return parseArray();
|
return parseArray();
|
||||||
} else {
|
} else {
|
||||||
@ -379,8 +379,8 @@ final class Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private AbstractConfigObject parseObject() {
|
private AbstractConfigObject parseObject(boolean hadOpenCurly) {
|
||||||
// invoked just after the OPEN_CURLY
|
// invoked just after the OPEN_CURLY (or START, if !hadOpenCurly)
|
||||||
Map<String, AbstractConfigValue> values = new HashMap<String, AbstractConfigValue>();
|
Map<String, AbstractConfigValue> values = new HashMap<String, AbstractConfigValue>();
|
||||||
ConfigOrigin objectOrigin = lineOrigin();
|
ConfigOrigin objectOrigin = lineOrigin();
|
||||||
boolean afterComma = false;
|
boolean afterComma = false;
|
||||||
@ -389,8 +389,13 @@ final class Parser {
|
|||||||
if (t == Tokens.CLOSE_CURLY) {
|
if (t == Tokens.CLOSE_CURLY) {
|
||||||
if (afterComma) {
|
if (afterComma) {
|
||||||
throw parseError("expecting a field name after comma, got a close brace }");
|
throw parseError("expecting a field name after comma, got a close brace }");
|
||||||
|
} else if (!hadOpenCurly) {
|
||||||
|
throw parseError("unbalanced close brace '}' with no open brace");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
} else if (t == Tokens.END && !hadOpenCurly) {
|
||||||
|
putBack(t);
|
||||||
|
break;
|
||||||
} else if (flavor != SyntaxFlavor.JSON && isIncludeKeyword(t)) {
|
} else if (flavor != SyntaxFlavor.JSON && isIncludeKeyword(t)) {
|
||||||
parseInclude(values);
|
parseInclude(values);
|
||||||
|
|
||||||
@ -405,7 +410,7 @@ final class Parser {
|
|||||||
&& afterKey == Tokens.OPEN_CURLY) {
|
&& afterKey == Tokens.OPEN_CURLY) {
|
||||||
// can omit the ':' or '=' before an object value
|
// can omit the ':' or '=' before an object value
|
||||||
valueToken = afterKey;
|
valueToken = afterKey;
|
||||||
newValue = parseObject();
|
newValue = parseObject(true);
|
||||||
} else {
|
} else {
|
||||||
if (!isKeyValueSeparatorToken(afterKey)) {
|
if (!isKeyValueSeparatorToken(afterKey)) {
|
||||||
throw parseError("Key may not be followed by token: "
|
throw parseError("Key may not be followed by token: "
|
||||||
@ -458,13 +463,24 @@ final class Parser {
|
|||||||
|
|
||||||
t = nextTokenIgnoringNewline();
|
t = nextTokenIgnoringNewline();
|
||||||
if (t == Tokens.CLOSE_CURLY) {
|
if (t == Tokens.CLOSE_CURLY) {
|
||||||
|
if (!hadOpenCurly) {
|
||||||
|
throw parseError("unbalanced close brace '}' with no open brace");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
} else if (t == Tokens.COMMA) {
|
} else if (t == Tokens.COMMA) {
|
||||||
// continue looping
|
// continue looping
|
||||||
afterComma = true;
|
afterComma = true;
|
||||||
} else {
|
} else if (hadOpenCurly) {
|
||||||
throw parseError("Expecting close brace } or a comma, got "
|
throw parseError("Expecting close brace } or a comma, got "
|
||||||
+ t);
|
+ t);
|
||||||
|
} else {
|
||||||
|
if (t == Tokens.END) {
|
||||||
|
putBack(t);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
throw parseError("Expecting end of input or a comma, got "
|
||||||
|
+ t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new SimpleConfigObject(objectOrigin,
|
return new SimpleConfigObject(objectOrigin,
|
||||||
@ -487,7 +503,7 @@ final class Parser {
|
|||||||
} else if (Tokens.isValue(t)) {
|
} else if (Tokens.isValue(t)) {
|
||||||
values.add(parseValue(t));
|
values.add(parseValue(t));
|
||||||
} else if (t == Tokens.OPEN_CURLY) {
|
} else if (t == Tokens.OPEN_CURLY) {
|
||||||
values.add(parseObject());
|
values.add(parseObject(true));
|
||||||
} else if (t == Tokens.OPEN_SQUARE) {
|
} else if (t == Tokens.OPEN_SQUARE) {
|
||||||
values.add(parseArray());
|
values.add(parseArray());
|
||||||
} else {
|
} else {
|
||||||
@ -515,7 +531,7 @@ final class Parser {
|
|||||||
if (Tokens.isValue(t)) {
|
if (Tokens.isValue(t)) {
|
||||||
values.add(parseValue(t));
|
values.add(parseValue(t));
|
||||||
} else if (t == Tokens.OPEN_CURLY) {
|
} else if (t == Tokens.OPEN_CURLY) {
|
||||||
values.add(parseObject());
|
values.add(parseObject(true));
|
||||||
} else if (t == Tokens.OPEN_SQUARE) {
|
} else if (t == Tokens.OPEN_SQUARE) {
|
||||||
values.add(parseArray());
|
values.add(parseArray());
|
||||||
} else {
|
} else {
|
||||||
@ -537,15 +553,25 @@ final class Parser {
|
|||||||
t = nextTokenIgnoringNewline();
|
t = nextTokenIgnoringNewline();
|
||||||
AbstractConfigValue result = null;
|
AbstractConfigValue result = null;
|
||||||
if (t == Tokens.OPEN_CURLY) {
|
if (t == Tokens.OPEN_CURLY) {
|
||||||
result = parseObject();
|
result = parseObject(true);
|
||||||
} else if (t == Tokens.OPEN_SQUARE) {
|
} else if (t == Tokens.OPEN_SQUARE) {
|
||||||
result = parseArray();
|
result = parseArray();
|
||||||
} else if (t == Tokens.END) {
|
} else {
|
||||||
|
if (flavor == SyntaxFlavor.JSON) {
|
||||||
|
if (t == Tokens.END) {
|
||||||
throw parseError("Empty document");
|
throw parseError("Empty document");
|
||||||
} else {
|
} else {
|
||||||
throw parseError("Document must have an object or array at root, unexpected token: "
|
throw parseError("Document must have an object or array at root, unexpected token: "
|
||||||
+ t);
|
+ t);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// the root object can omit the surrounding braces.
|
||||||
|
// this token should be the first field's key, or part
|
||||||
|
// of it, so put it back.
|
||||||
|
putBack(t);
|
||||||
|
result = parseObject(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
t = nextTokenIgnoringNewline();
|
t = nextTokenIgnoringNewline();
|
||||||
if (t == Tokens.END) {
|
if (t == Tokens.END) {
|
||||||
|
40
src/test/resources/equiv01/no-root-braces.conf
Normal file
40
src/test/resources/equiv01/no-root-braces.conf
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
"ints" : {
|
||||||
|
"fortyTwo" : 42,
|
||||||
|
"fortyTwoAgain" : 42
|
||||||
|
},
|
||||||
|
|
||||||
|
"floats" : {
|
||||||
|
"fortyTwoPointOne" : 42.1,
|
||||||
|
"fortyTwoPointOneAgain" : 42.1
|
||||||
|
},
|
||||||
|
|
||||||
|
"strings" : {
|
||||||
|
"abcd" : "abcd",
|
||||||
|
"abcdAgain" : "abcd",
|
||||||
|
"a" : "a",
|
||||||
|
"b" : "b",
|
||||||
|
"c" : "c",
|
||||||
|
"d" : "d",
|
||||||
|
"concatenated" : "null bar 42 baz true 3.14 hi"
|
||||||
|
},
|
||||||
|
|
||||||
|
"arrays" : {
|
||||||
|
"empty" : [],
|
||||||
|
"1" : [ 1 ],
|
||||||
|
"12" : [1, 2],
|
||||||
|
"123" : [1, 2, 3],
|
||||||
|
"ofString" : [ "a", "b", "c" ]
|
||||||
|
},
|
||||||
|
|
||||||
|
"booleans" : {
|
||||||
|
"true" : true,
|
||||||
|
"trueAgain" : true,
|
||||||
|
"false" : false,
|
||||||
|
"falseAgain" : false
|
||||||
|
},
|
||||||
|
|
||||||
|
"nulls" : {
|
||||||
|
"null" : null,
|
||||||
|
"nullAgain" : null
|
||||||
|
}
|
||||||
|
|
@ -87,6 +87,6 @@ class EquivalentsTest extends TestUtils {
|
|||||||
// it breaks every time you add a file, so you have to update it.
|
// it breaks every time you add a file, so you have to update it.
|
||||||
assertEquals(2, dirCount)
|
assertEquals(2, dirCount)
|
||||||
// this is the number of files not named original.*
|
// this is the number of files not named original.*
|
||||||
assertEquals(9, fileCount)
|
assertEquals(10, fileCount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ abstract trait TestUtils {
|
|||||||
|
|
||||||
// note: it's important to put {} or [] at the root if you
|
// note: it's important to put {} or [] at the root if you
|
||||||
// want to test "invalidity reasons" other than "wrong root"
|
// want to test "invalidity reasons" other than "wrong root"
|
||||||
private val invalidJsonInvalidConf = List[ParseTest]("", // empty document
|
private val invalidJsonInvalidConf = List[ParseTest](
|
||||||
"{",
|
"{",
|
||||||
"}",
|
"}",
|
||||||
"[",
|
"[",
|
||||||
@ -102,6 +102,10 @@ abstract trait TestUtils {
|
|||||||
"\"", // single quote by itself
|
"\"", // single quote by itself
|
||||||
"{ \"foo\" : }", // no value in object
|
"{ \"foo\" : }", // no value in object
|
||||||
"{ : 10 }", // no key in object
|
"{ : 10 }", // no key in object
|
||||||
|
" \"foo\" : ", // no value in object with no braces
|
||||||
|
" : 10 ", // no key in object with no braces
|
||||||
|
" \"foo\" : 10 } ", // close brace but no open
|
||||||
|
" \"foo\" : 10 [ ", // no-braces object with trailing gunk
|
||||||
"{ \"foo\" }", // no value or colon
|
"{ \"foo\" }", // no value or colon
|
||||||
"{ \"a\" : [ }", // [ is not a valid value
|
"{ \"a\" : [ }", // [ is not a valid value
|
||||||
ParseTest(true, "{ \"foo\" : 10, }"), // extra trailing comma (lift fails to throw)
|
ParseTest(true, "{ \"foo\" : 10, }"), // extra trailing comma (lift fails to throw)
|
||||||
@ -142,8 +146,7 @@ abstract trait TestUtils {
|
|||||||
"${ #comment }",
|
"${ #comment }",
|
||||||
"{ include \"bar\" : 10 }", // include with a value after it
|
"{ include \"bar\" : 10 }", // include with a value after it
|
||||||
"{ include foo }", // include with unquoted string
|
"{ include foo }", // include with unquoted string
|
||||||
"{ include : { \"a\" : 1 } }", // include used as unquoted key
|
"{ include : { \"a\" : 1 } }") // include used as unquoted key
|
||||||
"") // empty document again, just for clean formatting of this list ;-)
|
|
||||||
|
|
||||||
// We'll automatically try each of these with whitespace modifications
|
// We'll automatically try each of these with whitespace modifications
|
||||||
// so no need to add every possible whitespace variation
|
// so no need to add every possible whitespace variation
|
||||||
@ -170,10 +173,11 @@ abstract trait TestUtils {
|
|||||||
"""{ "foo" : { "bar" : "baz", "woo" : "w00t" }, "baz" : { "bar" : "baz", "woo" : [1,2,3,4], "w00t" : true, "a" : false, "b" : 3.14, "c" : null } }""",
|
"""{ "foo" : { "bar" : "baz", "woo" : "w00t" }, "baz" : { "bar" : "baz", "woo" : [1,2,3,4], "w00t" : true, "a" : false, "b" : 3.14, "c" : null } }""",
|
||||||
"{}")
|
"{}")
|
||||||
|
|
||||||
private val validConfInvalidJson = List[ParseTest](
|
private val validConfInvalidJson = List[ParseTest]("", // empty document
|
||||||
"""{ "foo" = 42 }""", // equals rather than colon
|
"""{ "foo" = 42 }""", // equals rather than colon
|
||||||
"""{ foo { "bar" : 42 } }""", // omit the colon for object value
|
"""{ foo { "bar" : 42 } }""", // omit the colon for object value
|
||||||
"""{ foo baz { "bar" : 42 } }""", // omit the colon with unquoted key with spaces
|
"""{ foo baz { "bar" : 42 } }""", // omit the colon with unquoted key with spaces
|
||||||
|
""" "foo" : 42 """, // omit braces on root object
|
||||||
"""{ "foo" : bar }""", // no quotes on value
|
"""{ "foo" : bar }""", // no quotes on value
|
||||||
"""{ "foo" : null bar 42 baz true 3.14 "hi" }""", // bunch of values to concat into a string
|
"""{ "foo" : null bar 42 baz true 3.14 "hi" }""", // bunch of values to concat into a string
|
||||||
"{ foo : \"bar\" }", // no quotes on key
|
"{ foo : \"bar\" }", // no quotes on key
|
||||||
|
Loading…
Reference in New Issue
Block a user