From dee2ac0587657e7dd8ec25ef8a9cef3c92a69359 Mon Sep 17 00:00:00 2001 From: Alexandre Rostovtsev Date: Thu, 23 Mar 2023 13:56:59 -0400 Subject: [PATCH] Relax JSON parser, ensure duplicate keys are overridden (#17868) Resolves #15605 Closes #17645. PiperOrigin-RevId: 514491743 Change-Id: I17ea9fb57682b668bff02bc64fefd75edb2cf2ee Co-authored-by: Adam Lavin --- src/main/java/net/starlark/java/lib/json/Json.java | 7 ++----- src/test/java/net/starlark/java/eval/testdata/json.star | 4 +++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/starlark/java/lib/json/Json.java b/src/main/java/net/starlark/java/lib/json/Json.java index c97972d8599f97..b366c740a18022 100644 --- a/src/main/java/net/starlark/java/lib/json/Json.java +++ b/src/main/java/net/starlark/java/lib/json/Json.java @@ -287,7 +287,8 @@ private void appendQuoted(String s) { + " a decimal point or an exponent. Although JSON has no syntax " + " for non-finite values, very large values may be decoded as infinity.\n" + "
  • a JSON object is parsed as a new unfrozen Starlark dict." - + " Keys must be unique strings.\n" + + " If the same key string occurs more than once in the object, the last" + + " value for the key is kept.\n" + "
  • a JSON array is parsed as new unfrozen Starlark list.\n" + "\n" + "Decoding fails if x is not a valid JSON encoding.\n", @@ -398,11 +399,7 @@ private Object parse() throws EvalException { } i++; // ':' Object value = parse(); - int sz = dict.size(); dict.putEntry((String) key, value); // can't fail - if (dict.size() == sz) { - throw Starlark.errorf("object has duplicate key: %s", Starlark.repr(key)); - } c = next(); if (c != ',') { if (c != '}') { diff --git a/src/test/java/net/starlark/java/eval/testdata/json.star b/src/test/java/net/starlark/java/eval/testdata/json.star index 51180b44860b2d..ff43163505cf78 100644 --- a/src/test/java/net/starlark/java/eval/testdata/json.star +++ b/src/test/java/net/starlark/java/eval/testdata/json.star @@ -80,6 +80,9 @@ assert_eq(json.decode('"\\u0123"'), 'ģ') assert_eq(json.decode('\t[\t1,\r2,\n3]\n'), [1, 2, 3]) # whitespace other than ' ' assert_eq(json.decode('\n{\t"a":\r1\t}\n'), {'a': 1}) # same, with dict assert_eq(json.decode(r'"\\\/\"\n\r\t"'), "\\/\"\n\r\t") # TODO(adonovan): test \b\f when Starlark/Java supports them +assert_eq(json.decode('{"x": 1, "x": 2}'), {"x": 2}) +assert_eq(json.decode('{"x": {"y": 1, "y": 2}}'), {"x": {"y": 2}}) +assert_eq(json.decode('{"x": {"y": 1, "z": 1}, "x": {"y": 2}}'), {"x": {"y": 2}}) # We accept UTF-16 strings that have been arbitrarily truncated, # as many Java and JavaScript programs emit them. @@ -123,7 +126,6 @@ assert_fails(lambda: json.decode('[1, 2}'), "got \"}\", want ',' or ']'") assert_fails(lambda: json.decode('{"one": 1'), "unexpected end of file") assert_fails(lambda: json.decode('{"one" 1'), 'after object key, got "1", want \':\'') assert_fails(lambda: json.decode('{"one": 1 "two": 2'), "in object, got ..\"., want ',' or '}'") -assert_fails(lambda: json.decode('{"x": 1, "x": 2}'), 'object has duplicate key: "x"') assert_fails(lambda: json.decode('{1:2}'), "got int for object key, want string") assert_fails(lambda: json.decode('{"one": 1,'), "unexpected end of file") assert_fails(lambda: json.decode('{"one": 1, }'), 'unexpected character "}"')