【问题标题】:Does JSON syntax allow duplicate keys in an object?JSON 语法是否允许对象中有重复的键?
【发布时间】:2019-01-27 03:50:14
【问题描述】:

这是有效的 json 吗?

{
    "a" : "x",
    "a" : "y"
}

http://jsonlint.com/ 说是的。

http://www.json.org/ 没有说它被禁止。

但显然它没有多大意义,不是吗? 大多数实现可能使用哈希表,因此无论如何它都会被覆盖。

【问题讨论】:

  • 如果您反序列化为Dictionary<string, string>,C# 的 Json.NET 会删除第一个密钥对
  • 如果有人来到这里希望找到在 JSON 字符串中查找重复值的解决方案,请查看free online json validator
  • jsonlint.com 说是。它没有,它删除除最后一个键值对之外的所有键值对,然后验证它,使其有效
  • 那么标准就坏了
  • 我使用键名“--”作为注释符,值是单个字符串行作为注释。所以我希望没有解析器会抱怨它。

标签: json standards


【解决方案1】:

简短的回答:可以,但不推荐。
长答案:这取决于你所说的有效......


[ECMA-404][1] “JSON 数据交换语法”没有提及重复名称(键)。
但是,[RFC 8259][2]“JavaScript 对象表示法 (JSON) 数据交换格式”说:

对象中的名称应该是唯一的。

在这种情况下应该必须理解为BCP 14中指定的:

应该这个词,或形容词“推荐”,意味着有 在特定情况下可能存在忽略 特定项目,但必须了解全部含义并 在选择不同的课程之前仔细权衡。


[RFC 8259][2] 解释了为什么唯一名称(键)是好的: > 从某种意义上说,名称都是唯一的对象是可互操作的 > 接收该对象的所有软件实现都将同意 > 名称-值映射。当对象中的名称不是 > 独特的,接收此类对象的软件的行为是 > 不可预测。许多实现报告姓氏/值对 > 仅限。其他实现报告错误或无法解析 > 对象,一些实现报告所有的名称/值对, > 包括重复项。

此外,正如 Serguei 在 cmets 中指出的:ECMA-262“ECMAScript® 语言规范”,内容如下:

如果对象中存在重复的名称字符串,则应覆盖同一键的词法前面的值。

换句话说,最后价值获胜。


尝试使用Java implementation by Douglas Crockford(JSON 的创建者)results in an exception 解析具有重复名称的字符串:

org.json.JSONException: Duplicate key "status"  at
org.json.JSONObject.putOnce(JSONObject.java:1076)

【讨论】:

  • JSON 应该是有效的 javascript,因此检查重复键是否是文字中的有效 JS 是相关的。 V8 似乎接受了它们:d8 -e 'x={"a":1,"a":2}; print(x.a);' 这将打印 2。
  • 另外,ECMA-262 规范为 JSON.parse() 明确表示 In the case where there are duplicate name Strings within an object, lexically preceding values for the same key shall be overwritten.(换句话说,最后一个值获胜)。
  • @BenCrowell:据我所知,JSON 不应该是有效的 JavaScript,在某些情况下它不是,请参阅 timelessrepo.com/json-isnt-a-javascript-subset。话虽如此,它当然深受 JavaScript 的启发(甚至在 JSON 规范中也是如此)。
  • 值得注意的是,当使用javascript“严格模式”时,如果有两个相同的键,chrome会使用第二个键值对而忽略第一个。 IE11 会抛出异常。
【解决方案2】:

来自the standard (p. ii):

预计其他标准会参考这一标准,严格遵循 JSON 文本格式,而 对各种编码细节施加限制。这样的标准可能需要特定的行为。 JSON 本身没有指定任何行为。

在标准(第 2 页)的更下方,JSON 对象的规范:

对象结构表示为一对围绕零个或多个名称/值对的花括号标记。 名称是一个字符串。每个名称后面都有一个冒号标记,将名称与值分开。一个 逗号标记将值与后面的名称分开。

它没有提到重复键无效或有效,因此根据规范,我可以安全地假设这意味着它们是允许的。

JSON 库的大多数实现不接受重复的键并不与标准冲突,因为第一个引号。

这里有两个与 C++ 标准库相关的例子。当将一些 JSON 对象反序列化为 std::map 时,拒绝重复键是有意义的。但是当将一些 JSON 对象反序列化为 std::multimap 时,正常接受重复键是有意义的。

【讨论】:

  • 我想我可以接受这个作为答案,虽然我喜欢提到@PatrickGoley 在 json.org 上它被称为一组键/值对,这意味着唯一性,这意味着它是无效。
  • @clamp json.org 不是标准,据我所知,它不是由 Emca International 运行的。 json.org 似乎是匿名呈现的。这是 规范:ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf 它在 json.org 上所说的内容不相关。
  • @clamp 考虑一下我刚刚添加的std::multimap 示例。它可以序列化为带有可能重复键的 JSON 对象。
  • @clamp 是一组键/值对并不排除重复名称。 {"a":1,"a":2} 是一组两个不同的键/值对。事实上,即使{"a":1,"a":1} 也可以被认为是一组恰好只有一个元素的键/值对。它被重复的事实可以被认为只是一个句法怪癖。更好的定义是,“对象是从字符串(名称)到值的部分函数。”
  • @TimothyShields 你链接到的那个标准说,“JSON 语法对用作名称的字符串没有任何限制,不要求名称字符串是唯一的,并且不赋予任何意义名称/值对的顺序"
【解决方案3】:

有 2 个文档指定 JSON 格式:

  1. http://json.org/
  2. https://www.rfc-editor.org/rfc/rfc7159

接受的答案引自第一个文档。我认为第一个文件更清晰,但第二个包含更多细节。

第二份文件说:

  1. 对象

对象结构表示为一对花括号 围绕零个或多个名称/值对(或成员)。一个名字是一个 细绳。每个名称后面都有一个冒号,用于分隔名称 从价值。单个逗号将值与以下值分开 姓名。 对象中的名称应该是唯一的。

所以不禁止重名,但不鼓励。

【讨论】:

    【解决方案4】:

    在处理同时接受 XML 和 JSON 的 API 时,我遇到了类似的问题,但没有记录它如何处理您期望的 JSON 中接受的重复键。

    以下是示例 JSON 的有效 XML 表示:

    <object>
      <a>x</a>
      <a>y</a>
    </object>
    

    将其转换为 JSON 后,您会得到以下信息:

    {
      "object": {
        "a": [
          "x",
          "y"
        ]
      }
    }
    

    从一种处理您可能称之为重复键的语言到另一种的自然映射可以作为潜在的最佳实践参考。

    希望对某人有所帮助!

    【讨论】:

      【解决方案5】:

      JSON 规范是这样说的:

      对象是一组无序的名称/值对。

      这里的重要部分是“无序的”:它意味着键的唯一性,因为唯一可以用来引用特定对的是它的键。

      此外,大多数 JSON 库会将 JSON 对象反序列化为哈希映射/字典,其中键保证唯一。当您反序列化具有重复键的 JSON 对象时会发生什么取决于库:在大多数情况下,您要么会收到错误,要么只会考虑每个重复键的最后一个值。

      例如,在 Python 中,json.loads('{"a": 1, "a": 2}') 返回 {"a": 2}。

      【讨论】:

      • 无序是否意味着唯一性?我认为 set 是这里的关键词
      • 无序的颜色集合:蓝色、绿色、绿色、蓝色、红色、蓝色、绿色 - 它有重复项。
      • 您引用的文本短语“对象是一组无序的名称/值对”,未出现在 JSON 规范中...
      • 我现在看到你引用了 json.org。它“接近”官方,但不是规范。页面顶部有一个规范链接,在 json.org 上逐字重复。如果搜索规范,“unordered”这个词不会出现,“set”这个词只出现在与 JSON 对象无关的上下文中。
      • 请注意,它说的是“无序集名称/值对”,而不是名称。也就是说,{ (a,b), (a,c) } 是一个唯一的集合。所以在技术上,在 json.org 定义下 {"a":1,"a":2} 是有效的,但 {"a":1,"a":2,"a":1} 不是。还要注意ECMA-404(实际标准)避免使用“set”这个词:An object structure is represented as a pair of curly bracket tokens surrounding zero or more name/value pairs.
      【解决方案6】:

      发布和回答是因为有很多过时的想法和对标准的混淆。截至 2017 年 12 月,有两个相互竞争的标准:

      RFC 8259 - https://www.rfc-editor.org/rfc/rfc8259

      ECMA-404 - http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf

      json.org 建议 ECMA-404 是 the 标准,但该站点似乎不是权威。虽然我认为将 ECMA 视为权威是公平的,但这里重要的是,标准之间的唯一区别(关于唯一密钥)是 RFC 8259 规定密钥应该是唯一的,而 ECMA-404表示它们不需要是唯一的。

      RFC-8259:

      “对象中的名称应该是唯一的。”

      所有大写的“应该”一词在 RFC 世界中具有含义,在另一个标准(BCP 14,RFC 2119 - https://www.rfc-editor.org/rfc/rfc2119)中明确定义为,

      1. 应该这个词或形容词“推荐”的意思是 在特定情况下可能存在忽略的正当理由 一个特定的项目,但必须了解其全部含义并 在选择不同的课程之前仔细权衡。

      ECMA-404:

      "JSON 语法对使用的字符串没有任何限制 作为名称,不要求名称字符串是唯一的,并且不 为名称/值对的顺序赋予任何意义。”

      因此,无论您如何对其进行切片,它在语法上都是有效的 JSON。

      RFC 8259 中给出唯一密钥推荐的原因是,

      名称都是唯一的对象在某种意义上是可互操作的 接收该对象的所有软件实现都将同意 名称-值映射。当对象中的名称不是 唯一的,接收这样一个对象的软件的行为是 不可预测的。许多实现报告姓氏/值对 只要。其他实现报告错误或无法解析 对象,一些实现报告所有的名称/值对, 包括重复。

      换句话说,从 RFC 8259 的角度来看,它是有效的,但您的解析器可能会出错,并且无法保证哪个值(如果有)将与该键配对。从 ECMA-404 的角度来看(我个人认为这是权威),它是有效的。对我来说,这意味着任何拒绝解析它的解析器都会被破坏。它至少应该根据这两个标准进行解析。但是,无论如何,它如何变成您选择的本机对象,完全取决于环境和情况,而这些都不是一开始的标准。

      【讨论】:

      • json.org 实际上早于 ECMA 标准化。我相信它实际上是由 Crockford 自己设置的(这就是为什么它为他的书提供了一个无耻的插件)。那时它是 JSON 的权威。
      【解决方案7】:

      应该是唯一的并不意味着必须是唯一的。但是,如前所述,一些解析器会失败,而其他解析器只会使用最后解析的值。但是,如果对规范进行了一些清理以允许重复,那么我可以看到一个用途,您可能有一个事件处理程序将 JSON 转换为 HTML 或其他格式......在这种情况下,它完全有效解析 JSON 并创建另一种文档格式...

      [
        "div":
        {
          "p": "hello",
          "p": "universe"
        },
        "div":
        {
          "h1": "Heading 1",
          "p": "another paragraph"
        }
      ]
      

      然后可以轻松解析为 html,例如:

      <body>
       <div>
        <p>hello</p>
        <p>universe</p>
       </div>
       <div>
        <h1>Heading 1</h1>
        <p>another paragraph</p>
       </div>
      </body>
      

      我可以看到问题背后的原因,但就目前而言......我不会相信它。

      【讨论】:

      • 第一个示例的数组缺少逗号。此外,它与自身不一致。如果您打算将字典用作有序集合,那么您的外部数组也应该只是一个对象。即{"div":{"p":"hello","p":"universe"}, "div":{"h1":"Heading 1","p":"another paragraph"}}。现在,很多人和框架都将 JSON 对象视为无序字典,但 JavaScript 和 e.g. MongoDB 的 API 依赖于字典中键的排序,因此您所建议的(有序字典)并非闻所未闻。你只需要一个专门的解析器。
      • 有序字典仍有唯一键。
      • 数组可以有关联的值吗?
      【解决方案8】:

      它没有在ECMA JSON standard 中定义。一般来说,标准中缺乏定义意味着“不要指望这种方式在任何地方都以相同的方式工作。”

      如果您是一名赌徒,“许多”JSON 引擎将允许重复并简单地使用最后指定的值。这个:

      var o = {"a": 1, "b": 2, "a": 3}
      

      变成这样:

      Object {a: 3, b: 2}
      

      但如果你不是赌徒,别指望它!

      【讨论】:

        【解决方案9】:

        问目的,有不同的答案:

        使用 JSON 序列化对象(JavaScriptObjectNotation),每个字典元素映射到一个单独的对象属性,因此为同一属性定义值的不同条目没有意义。

        但是,我从一个非常具体的用例中遇到了同样的问题: 为 API 测试编写 JSON 示例,我想知道如何在不破坏可用性的情况下将 cmets 添加到我们的 JSON 文件中。 JSON规范不知道cmets,所以我想出了一个非常简单的方法:

        使用重复键来评论我们的 JSON 示例。 示例:

        { "property1" : "value1", "REMARK" : "... prop1 controls ...", "property2" : "value2", "REMARK" : "... value2 raises an exception ...", }

        我们使用的 JSON 序列化程序对这些“REMARK”重复没有任何问题,我们的应用程序代码只是忽略了这个小开销。

        因此,即使在应用层没有任何意义,但这些副本为我们提供了一种有价值的解决方法,可以在不破坏 JSON 可用性的情况下将 cmets 添加到我们的测试样本中。

        【讨论】:

        • 这是个坏主意。通过包含重复键,即使您不需要读取其中的数据,您也依赖于未定义的行为。一些解析器,例如 Crockford 的 JSON-java 解析器,会抛出异常并拒绝解析数据。
        • 实际上在我们的环境中工作得很好,所以可以满足我的需求,尽管我同意你的观点,这是某种不合规格的 ;)
        • @RichardSmith 我想说解析器和更新的 ES 规范已经定义了这种行为。
        【解决方案10】:

        标准确实是这样说的:

        编程语言在是否支持对象方面存在很大差异,以及 如果是,这些对象提供了哪些特征和约束。这 对象系统的模型可能大相径庭,并且正在继续 发展。 JSON 反而提供了一种简单的表示法来表达 名称/值对的集合。大多数编程语言都会有 表示此类集合的一些功能,可以按名称进行 比如记录、结构、字典、映射、哈希或对象。

        该错误至少在 node.js 中。此代码在 node.js 中成功。

        try {
             var json = {"name":"n","name":"v"};
             console.log(json); // outputs { name: 'v' }
        } catch (e) {
             console.log(e);
        }
        

        【讨论】:

        • 这不是一个错误,您引用的部分解释了为什么它不是:不同的语言表现不同,JSON 解析器将为该语言做最自然的事情。无论如何,这个答案并没有添加 user454322 上面没有说过的任何内容。
        【解决方案11】:

        根据 RFC-7159,由 Internet 工程任务组 (IETF) 发布的 JSON 的当前标准,声明“对象中的名称应该是唯一的”。然而,根据定义 IETF 文档中使用的术语的 RFC-2119,“应该”一词实际上意味着“......在特定情况下可能存在忽略特定项目的正当理由,但必须理解其全部含义并在选择不同的课程之前仔细权衡。”这实质上意味着虽然建议使用唯一键,但这不是必须的。我们可以在 JSON 对象中有重复的键,它仍然有效。

        从实际应用中,我看到当在 JSON 中找到重复键时,会考虑最后一个键的值。

        【讨论】:

        • JSON 没有 cmets - 最终值上方的“重复”相当于“注释掉”。这是使用它的一个正当理由。
        【解决方案12】:

        在 C# 中,如果您反序列化为 Dictionary&lt;string, string&gt;,它将采用最后一个键值对:

        string json = @"{""a"": ""x"", ""a"": ""y""}";
        var d = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
        // { "a" : "y" }
        

        如果你尝试反序列化为

        class Foo
        {
            [JsonProperty("a")]
            public string Bar { get; set; }
        
            [JsonProperty("a")]
            public string Baz { get; set; }
        }
        
        var f = JsonConvert.DeserializeObject<Foo>(json);
        

        你得到一个Newtonsoft.Json.JsonSerializationException 异常。

        【讨论】:

        • 我猜这是大多数(如果不是全部)实现所做的,但它不能回答我的问题,如果它在 JSON 规范中有效。
        • @svidgen:这甚至不是微软的实现……它是一个第三方库。
        • @BoltClock 啊,触摸。
        猜你喜欢
        • 2016-03-21
        • 2013-08-22
        • 2015-06-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-07
        相关资源
        最近更新 更多