JSON语法是否允许对象中的重复键?

时间:2014-02-17 15:16:02

标签: json standards

这是有效的json吗?

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

http://jsonlint.com/说是。

http://www.json.org/没有说明被禁止的事情。

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

12 个答案:

答案 0 :(得分:102)

答案简短:是的,但不建议使用。
答案很长:这取决于你所说的有效......


JSON Data Interchange Format (ECMA-404)没有说明重复的名称(键)。

但是,The JavaScript Object Notation (JSON) Data Interchange Format) (RFC7159)说:

  

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

在此上下文中, 必须理解为RFC 2119

中指定的内容
  

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



RFC 7159解释了为什么唯一名称(键)是好的:

  

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

     

已经观察到JSON解析库是否存在差异   或不是它们使对象成员的排序对调用可见   软件。行为不依赖于成员的实现   订购将是互操作的,因为他们不会是   受到这些差异的影响。




另外,正如Serguei在评论中指出的那样:ECMA-262“ECMAScript®语言规范”,内容如下:

  

如果对象中存在重复的名称字符串,   应覆盖相同密钥的词法前置值。

     

(换句话说,last-value-wins)。




尝试使用Java implementation by Douglas Crockford(JSON的创建者)解析具有重复名称的字符串会导致异常:

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

答案 1 :(得分:100)

来自the standard (p. ii)

  

预计其他标准将引用此标准,严格遵守JSON文本格式   对各种编码细节施加限制。这些标准可能需要特定的行为。 JSON   本身不指定任何行为。

在标准中进一步向下(第2页),JSON对象的规范:

  

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

     

Diagram for JSON Object

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

由于第一个引用,大多数JSON库的实现接受重复键都不会与标准冲突。

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

答案 2 :(得分:18)

有2个文件指定JSON格式:

  1. http://json.org/
  2. https://tools.ietf.org/html/rfc7159
  3. 接受的答案引自第1份文件。我认为第一个文件更清楚,但第二个文件包含更多细节。

    第二份文件说:

      
        
    1. 物件

           

      对象结构表示为一对花括号   包含零个或多个名称/值对(或成员)。名字是   串。每个名称后面都有一个冒号,分隔名称   从价值。单个逗号将值与后续值分开   名称。 对象中的名称应该是唯一的。

    2.   

    因此不禁止重复名称,但不鼓励。

答案 3 :(得分:8)

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

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

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

当它转换为JSON时,您将获得以下内容:

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

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

希望能帮助别人!

答案 4 :(得分:6)

JSON规范说明了这一点:

  

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

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

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

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

答案 5 :(得分:3)

应该是唯一的并不意味着必须是唯一的。但是,如上所述,某些解析器会失败,而其他解析器只会使用解析的最后一个值。但是,如果规范被清理了一点以允许重复,那么我可以看到一个用途,你可能有一个事件处理程序,它将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>

我可以看到问题背后的原因,但现在看来......我不会相信它。

答案 6 :(得分:2)

要求目的,有不同的答案:

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

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

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

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

我们使用的JSON序列化器对这些&#34; REMARK&#34;没有任何问题。重复和我们的应用程序代码只是忽略了这个小开销。

因此,即使应用程序层没有任何意义,这些重复项也为我们的测试样本添加注释提供了一个有价值的解决方法,而不会破坏JSON的可用性。

答案 7 :(得分:1)

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

如果您是赌徒,“许多”JSON引擎将允许复制并只使用最后指定的值。这样:

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

成为这个:

Object {a: 3, b: 2}

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

答案 8 :(得分:1)

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

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

答案 9 :(得分:0)

在C#中,如果您反序列化为Dictionary<string, string>,则需要最后一个键值对:

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例外。

答案 10 :(得分:0)

标准确实这样说:

  

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

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

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

答案 11 :(得分:0)

发布和回答,因为关于标准存在很多过时的想法和困惑。截至2017年12月,有两个相互竞争的标准:

RFC 8259-https://tools.ietf.org/html/rfc8259

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

json.org建议ECMA-404是 标准,但该网站似乎不是权威机构。尽管我认为将ECMA视为权威是公平的,但这里重要的是,标准(关于唯一密钥)之间的唯一区别是RFC 8259表示密钥是唯一的,而ECMA-404说他们不需要是唯一的。

RFC-8259:

  

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

在所有大写字母中,“应该”一词在RFC世界中具有含义,在另一个标准(BCP 14,RFC 2119-https://tools.ietf.org/html/rfc2119)中专门定义为

  
      
  1. 应该词或形容词“推荐”的意思是   在特定情况下可能存在正当理由要忽略   特定项目,但必须理解全部含义并   在选择其他路线之前,请仔细权衡。
  2.   

ECMA-404:

  

“ JSON语法不对所使用的字符串施加任何限制   作为名称,不需要名称字符串是唯一的,并且不   为名称/值对的排序赋予任何意义。”

因此,无论您如何切片,它都是语法有效的JSON 。

给出RFC 8259中唯一密钥建议的原因是

  

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

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