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


205

这是有效的json吗?

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

http://jsonlint.com/表示同意。

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

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


1
如果您反序列化为ADictionary<string, string>
Sam Leach,

如果有人到达这里希望找到解决方案以在JSON字符串中查找重复值,请查看免费的在线JSON验证器
Pepijn Olivier 2015年

jsonlint.com表示同意它不会,它将删除最后一个键值对之外的所有键,然后对其进行验证,从而使其有效
Tim

7
然后标准被打破了
布拉德·托马斯

1
我使用键名“-”作为注释器,值是单个字符串行作为注释。所以我希望没有解析器会抱怨它。
Lothar

Answers:


126

标准(第ii页)

预计其他标准将严格遵循JSON文本格式,同时对各种编码详细信息施加限制,以引用此标准。此类标准可能需要特定的行为。JSON本身未指定任何行为。

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

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

JSON对象图

它没有提及重复密钥无效或有效,因此根据规范,我可以放心地认为这意味着它们是允许的。

由于首引号,大多数JSON库实现接受重复键并不与标准冲突。

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


5
我想我可以接受这个答案,尽管我喜欢提到@PatrickGoley,在json.org上它被称为一组键/值对,这意味着唯一性,这意味着它无效。
夹紧

8
@clamp json.org不是标准的,据我所知,它不是由Emca International运行的。json.org似乎是匿名提供的。这是规格:ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf它说json.org什么是不相关的。
蒂莫西·希尔兹

5
@clamp考虑一下std::multimap我刚刚添加的示例。可以使用可能重复的密钥将其序列化为JSON对象。
蒂莫西·希尔兹

1
@clamp是一组键/值对,并不排除重复的名称。{"a":1,"a":2}是一组两个不同的键/值对。实际上,甚至{"a":1,"a":1}可以将其视为一组恰好只有一个元素的键/值对。重复它的事实可以被认为只是一种语法怪癖。更好的定义是“对象是从字符串(名称)到值的部分函数”。
Marcelo Cantos '18

2
@TimothyShields和您链接的标准说,“ JSON语法不对用作名称的字符串施加任何限制,不要求名称字符串唯一,并且不对名称/值对的排序赋予任何意义”
查尔斯

135

简短的答案:是,但不建议这样做。
长答案:取决于您所说的有效...


ECMA-404 “ JSON数据交换语法”未提及重复的名称(键)。


但是,RFC 8259 “ JavaScript对象符号(JSON)数据交换格式”表示:

对象中的名称应唯一。

在这种情况下应该作为规定必须理解BCP 14

应该使用这个词,或形容词“推荐”,这意味着在特定情况下可能存在忽略某个特定项目的正当理由,但是在选择其他方法之前,必须理解并仔细权衡所有含义。


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

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



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

如果一个对象中有重复的名称字符串,则相同键的词法先前值应被覆盖。

换句话说,最终值就是胜利。


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

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

1
JSON应该是有效的javascript,因此与检查重复键是否为文字中的有效JS有关。V8似乎接受他们:d8 -e 'x={"a":1,"a":2}; print(x.a);'这将打印2
本韦尔

5
另外,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.(换句话说最后的价值获胜)。
Serguei

4
@BenCrowell:据我所知,JSON不应该是有效的JavaScript,在某些情况下不是,请参阅timelessrepo.com/json-isnt-a-javascript-subset。话虽如此,它当然在很大程度上受到了JavaScript的启发(甚至在JSON规范中也是如此)。
Simon Touchtech '16

2
值得注意的是,当使用javascript“严格模式”时,如果有两个相同的键,chrome将使用第二个键值对,而忽略第一个。IE11将引发异常。
Shahar

18

有2个文档指定JSON格式:

  1. http://json.org/
  2. https://tools.ietf.org/html/rfc7159

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

第二份文件说:

  1. 对象

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

因此,不禁止使用重复的名称,但是不建议使用。


10

在处理同时接受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}


23
无序暗示唯一性吗?我认为集最关键的词在这里
帕特里克Goley

8
颜色的无序集合:蓝色,绿色,绿色,蓝色,红色,蓝色,绿色-具有重复项。
蒂莫西·希尔兹

5
您引用的文本短语“对象是名称/值对的无序集合”不会出现在JSON规范中……
Timothy Shields 2014年

6
现在我知道您在引用json.org。它与官方“接近”,但不是规范。页面顶部有指向规范的链接,该链接在json.org上逐字重复。如果您搜索规范,则不会出现单词“ unordered”,而仅在与JSON对象无关的上下文中出现单词“ set”。
蒂莫西·希尔兹

5
请注意,它说的是“ 名称/值对的无序集合”,不是名称对。也就是说,{ (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.
Serguei

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>

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


您第一个示例的数组缺少逗号。而且,它与自身不一致。如果要使用字典作为有序集合,则外部数组也应该只是一个对象。即,{"div":{"p":"hello","p":"universe"}, "div":{"h1":"Heading 1","p":"another paragraph"}}。现在,许多人和框架将JSON对象视为无序字典,但是JavaScript和MongoDB的API依赖于字典中键的顺序,因此您所听说的(有序字典)并不是闻所未闻的。您只需要一个专门的解析器。
Binki'7

2

出于目的,有不同的答案:

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

但是,我从一个非常特定的用例中遇到了同样的问题:编写用于API测试的JSON示例,我想知道如何在不破坏可用性的情况下向我们的JSON文件添加注释。JSON规范不知道注释,因此我想出了一种非常简单的方法:

要使用重复键来注释我们的JSON示例。例:

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

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

因此,即使在应用程序层上没有意义,这些重复项对我们来说也提供了一种宝贵的解决方法,可以在不破坏JSON可用性的情况下向我们的测试示例添加注释。


这是一个坏主意。通过包含重复键,即使您不需要读取它们中的数据,您也可以依赖未定义的行为。一些解析器(例如Crockford的JSON-java解析器)会引发异常并拒绝解析数据。
理查德·史密斯

实际上在我们的环境中可以正常工作,因此可以满足我的需求,即使我同意您的意见,这
也不合

@RichardSmith我想说解析器和更新的ES规范已经定义了行为。
Binki'7

2

发布和回答,因为关于标准存在很多过时的想法和混乱。截至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. 应该使用这个词,或形容词“推荐”,这意味着在特定情况下可能存在忽略特定项目的正当理由,但是在选择其他方法之前,必须理解并仔细权衡所有含义。

ECMA-404:

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

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

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

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

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


json.org实际上早于ECMA标准化。我相信它实际上是由克罗克福德本人设置的(这就是为什么它的书有一个无耻的插件)。那时,这是JSON的权限。
最大

1

ECMA JSON标准中未定义它。通常来说,标准中缺乏定义意味着“不要在任何地方都以相同的方式来工作”。

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

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

变成这个:

Object {a: 3, b: 2}

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


1

该标准确实说明了这一点:

编程语言在是否支持对象以及是否支持对象方面存在很大差异。对象系统的模型可能大相径庭,并且还在不断发展。JSON提供了一种简单的表示法来表示名称/值对的集合。大多数编程语言都具有一些表示此类集合的功能,可以按记录,结构,字典,映射,哈希或对象之类的名称命名。

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

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

1
这不是错误,您引用的部分解释了为什么不是这样:不同的语言表现不同,JSON解析器将执行该语言最自然的操作。无论如何,此答案不会添加user454322尚未在上面说的任何内容。
理查德·史密斯

1

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

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


0

在C#中,如果您反序列化为a,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例外。


2
我猜这是大多数(如果不是全部)实现的功能,但是它并没有回答我的问题,即从JSON规范开始它是否有效。
夹紧

@svidgen:这甚至不是Microsoft的实现,它是第三方库。
BoltClock

@BoltClock啊,感动。
svidgen 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.