diff --git a/externals/jsonlint/src/Seld/JsonLint/JsonParser.php b/externals/jsonlint/src/Seld/JsonLint/JsonParser.php --- a/externals/jsonlint/src/Seld/JsonLint/JsonParser.php +++ b/externals/jsonlint/src/Seld/JsonLint/JsonParser.php @@ -374,7 +374,7 @@ case 17: $yyval->token = $tokens[$len-2]; $key = $tokens[$len][0] === '' ? '_empty_' : $tokens[$len][0]; - if (($this->flags & self::DETECT_KEY_CONFLICTS) && isset($tokens[$len-2]->{$key})) { + if (($this->flags & self::DETECT_KEY_CONFLICTS) && isset($tokens[$len-2][$key])) { $errStr = 'Parse error on line ' . ($yylineno+1) . ":\n"; $errStr .= $this->lexer->showPosition() . "\n"; $errStr .= "Duplicate key: ".$tokens[$len][0]; diff --git a/src/parser/PhutilJSONParser.php b/src/parser/PhutilJSONParser.php --- a/src/parser/PhutilJSONParser.php +++ b/src/parser/PhutilJSONParser.php @@ -8,6 +8,12 @@ */ final class PhutilJSONParser { + private $allowDuplicateKeys = false; + + public function setAllowDuplicateKeys($allow_duplicate_keys) { + $this->allowDuplicateKeys = $allow_duplicate_keys; + } + public function parse($json) { $jsonlint_root = phutil_get_library_root('phutil').'/../externals/jsonlint'; require_once($jsonlint_root.'/src/Seld/JsonLint/JsonParser.php'); @@ -21,18 +27,30 @@ $parser = new JsonLintJsonParser(); try { - return $parser->parse($json); + return $parser->parse($json, $this->getFlags()); } catch (JsonLintParsingException $ex) { $details = $ex->getDetails(); $message = preg_replace("/^Parse error .*\\^\n/s", '', $ex->getMessage()); throw new PhutilJSONParserException( - $details['loc']['last_line'], - $details['loc']['last_column'], - $details['token'], - $details['expected'], + idx(idx($details, 'loc', array()), 'last_line'), + idx(idx($details, 'loc', array()), 'last_column'), + idx($details, 'token'), + idx($details, 'expected'), $message); } } + private function getFlags() { + $flags = 0; + + if ($this->allowDuplicateKeys) { + $flags |= JsonLintJsonParser::ALLOW_DUPLICATE_KEYS; + } else { + $flags |= JsonLintJsonParser::DETECT_KEY_CONFLICTS; + } + + return $flags; + } + } diff --git a/src/parser/__tests__/PhutilJSONParserTestCase.php b/src/parser/__tests__/PhutilJSONParserTestCase.php --- a/src/parser/__tests__/PhutilJSONParserTestCase.php +++ b/src/parser/__tests__/PhutilJSONParserTestCase.php @@ -85,4 +85,29 @@ } } + public function testDuplicateKeys() { + $parser = new PhutilJSONParser(); + + $tests = array( + '{"foo": "bar", "foo": "baz"}' => array('foo' => 'bar'), + ); + + foreach ($tests as $input => $expect) { + $parser->setAllowDuplicateKeys(true); + $this->assertEqual( + $expect, + $parser->parse($input), + 'Parsing JSON: '.$input); + + $parser->setAllowDuplicateKeys(false); + $caught = null; + try { + $parser->parse($input); + } catch (Exception $ex) { + $caught = $ex; + } + $this->assertTrue($caught instanceof PhutilJSONParserException); + } + } + }