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

这是有效的json吗?

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

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

http://www.json.org/没有说任何关于它是禁止的。

但是很明显这没什么意义,不是吗? 大多数实现可能使用哈希表,所以无论如何它都会被覆盖

157722 次浏览

在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异常。

标准(p. ii):

预计其他标准将参考此标准,严格遵循JSON文本格式,而 对各种编码细节施加限制。这种标准可能需要具体的行为。JSON 本身不指定任何行为。< / p >

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

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

 JSON对象的图表

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

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

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

JSON规范是这样说的:

对象是名称/值对的无序集合。

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

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

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

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

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

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

变成这样:

Object {a: 3, b: 2}

但如果你不是一个赌徒,别指望了!

简单回答:可以,但不建议使用。
长一点的回答是:这取决于你如何定义有效…

JSON数据交换语法;没有说任何关于重复的名称(键)。

然而,RFC 8259 " JavaScript对象符号(JSON)数据交换格式"说:

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

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

这个词,或者形容词“被推荐的”,意思是在那里 在特定情况下可能存在有效的理由忽略 特定的项目,但必须充分理解的含义和

.在选择不同的路线之前要仔细权衡

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

如果一个对象的名称都是唯一的,那么这个对象就是可互操作的 所有接收该对象的软件实现都会同意 名称-值映射。当对象中的名称不是时 唯一的,软件接收这样一个对象的行为是 不可预测的。许多实现报告姓氏/值对 只有。属性的其他实现报告错误或未能解析 对象,一些实现报告所有的名称/值对, 包括重复。< / p >

此外,正如Serguei在评论中指出的那样:ecma - 262 "ECMAScript®语言规范"

如果一个对象中有重复的name string,则应覆盖相同键的词法前面的值。

换句话说,最后的价值才是赢家。


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

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

标准是这么说的:

编程语言在是否支持对象和 如果是,对象提供了什么特征和约束。的 对象系统的模型可以有很大的分歧,而且还在继续如此 进化。JSON提供了一个简单的表示符号 名称/值对的集合。大多数编程语言都有 一些用于表示此类集合的特性,这些集合可以按名称命名 例如record, struct, dict, map, hash或object.

这个错误至少在node.js中。这段代码在node.js中成功了。

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

问目的,有不同的答案:

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

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

< p > 使用重复的键来注释JSON样本。 例子:< / p > < p > <代码> { "property1": "value1", "REMARK": "…"Prop1控制…", "property2": "value2", "REMARK": "…Value2引发异常…", 代码}< / > < / p >

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

因此,尽管在应用层没有任何意义,但这些副本为我们提供了一个有价值的解决方案,可以在不破坏JSON可用性的情况下向测试示例添加注释。

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

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

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

当它被转换成JSON时,你会得到以下内容:

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

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

希望这能帮助到别人!

有两个文档指定JSON格式:

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

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

第二份文件说:

  1. 对象
对象结构用一对花括号表示 包围零个或多个名称/值对(或成员)。名字就是 字符串。每个名称后面都有一个冒号,分隔名称 从值。一个逗号将值与后面的字符分隔开 的名字。对象中的名称应该是唯一的。 < / p >

所以不禁止使用重复的名字,但不鼓励使用。

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

我知道这个问题背后的原因,但就目前情况而言……我不会相信它。

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

从实际应用中,我已经看到,当在JSON中发现重复的键时,会考虑来自最后一个键的值。

因为有很多过时的想法和对标准的困惑。截至2017年12月,有两个相互竞争的标准:

RFC 8259 - __abc0

Ecma-404 - __abc0

json.org建议ECMA-404是标准,但这个网站似乎不是一个权威。虽然我认为认为ECMA是权威是公平的,但这里重要的是,标准之间的唯一区别(关于唯一键)是RFC 8259说键应该是唯一的,而ECMA-404说它们是不是必需的是唯一的。

rfc - 8259:

对象中的名称必须是唯一的。

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

    这个词,或者形容词“推荐的”,意思是 在特定情况下,可能存在忽视的正当理由 一个特定的项目,但必须理解全部的含义和 .在选择不同的路线之前仔细权衡

ecma - 404:

JSON语法对所使用的字符串没有任何限制 作为名称,不要求名称字符串是唯一的,也不要求 对名称/值对的排序赋予任何重要性。quot;

所以,无论你如何分割它,它是语法上有效的JSON

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

如果一个对象的名称都是唯一的,那么这个对象就是可互操作的 所有接收该对象的软件实现都会同意 名称-值映射。当对象中的名称不是时 唯一的,软件接收这样一个对象的行为是 不可预测的。许多实现报告姓氏/值对 只有。属性的其他实现报告错误或未能解析 对象,一些实现报告所有的名称/值对, 包括重复。< / p >

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