Разрешает ли синтаксис JSON дублировать ключи в объекте?

205

Это действительный JSON?

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

http://jsonlint.com/ говорит да.

http://www.json.org/ ничего не говорит о том, что это запрещено.

Но, очевидно, это не имеет особого смысла, не так ли? В большинстве реализаций, вероятно, используется хеш-таблица, поэтому она все равно переопределяется.

зажим
источник
1
C # Json.NET удаляет первую пару ключей, если вы десериализуете вDictionary<string, string>
Сэм Лич
Если кто-то прибывает сюда в надежде найти решение для поиска повторяющихся значений в строках JSON, воспользуйтесь бесплатным онлайн-валидатором json
Pepijn Olivier
jsonlint.com говорит да. это не так, он удаляет все, кроме последней пары ключ-значение, а затем проверяет ее, что делает ее действительной
Тим
7
Тогда стандарт нарушается
Брэд Томас
1
Я использовал имя ключа «-» в качестве комментария, а значение - это одна строка в качестве комментария. Поэтому я надеюсь, что ни один парсер не будет жаловаться на это.
Lothar

Ответы:

126

Из стандарта (стр. II) :

Ожидается, что другие стандарты будут ссылаться на него, строго придерживаясь формата текста JSON, налагая ограничения на различные детали кодирования. Такие стандарты могут требовать определенного поведения. Сам JSON не определяет поведение.

Далее в стандарте (стр. 2) спецификация для объекта JSON:

Структура объекта представляется в виде пары фигурных скобок, окружающих ноль или более пар имя / значение. Имя - это строка. Один маркер двоеточия следует за каждым именем, отделяя имя от значения. Один токен запятой отделяет значение от следующего имени.

Диаграмма для объекта JSON

В нем не упоминается, что дубликаты ключей являются недействительными или допустимыми, поэтому в соответствии со спецификацией я бы смело предположил, что они разрешены.

То, что большинство реализаций библиотек JSON не принимают дубликаты ключей, не противоречит стандарту из-за первой цитаты.

Вот два примера, относящихся к стандартной библиотеке C ++. При десериализации некоторого объекта JSON в a 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}можно рассматривать как набор пар ключ / значение, в которых есть только один элемент. Тот факт, что это повторяется, можно рассматривать как просто синтаксическую причуду. Лучшим определением было бы: «Объект - это частичная функция от строк (имен) до значений».
Марсело Кантос
2
@TimothyShields И тот стандарт, на который вы ссылались, гласит: «Синтаксис JSON не накладывает никаких ограничений на строки, используемые в качестве имен, не требует, чтобы строки имен были уникальными, и не назначал никакого значения упорядочению пар имя / значение».
Чарльз
135

Краткий ответ: да, но не рекомендуется.
Длинный ответ: это зависит от того, что вы называете действительным ...


ECMA-404 «Синтаксис обмена данными JSON» ничего не говорит о дублированных именах (ключах).


Тем не менее, в RFC 8259 «Формат обмена данными JavaScript (JSON)» говорится:

Имена внутри объекта ДОЛЖНЫ быть уникальными.

В этом контексте СЛЕДУЕТ понимать как указано в BCP 14 :

СЛЕДУЕТ Это слово или прилагательное «РЕКОМЕНДУЕМЫЕ» означают, что могут существовать веские причины в определенных обстоятельствах игнорировать конкретный элемент, но все значения должны быть поняты и тщательно взвешены, прежде чем выбрать другой курс.


RFC 8259 объясняет, почему уникальные имена (ключи) хороши:

Объект, все имена которого уникальны, является функционально совместимым в том смысле, что все программные реализации, получающие этот объект, согласятся с отображениями имя-значение. Когда имена внутри объекта не являются уникальными, поведение программного обеспечения, получающего такой объект, непредсказуемо. Многие реализации сообщают только пару фамилия / значение. Другие реализации сообщают об ошибке или не могут проанализировать объект, а некоторые реализации сообщают обо всех парах имя / значение, включая дубликаты.



Также, как отметил Сергей в комментариях: ECMA-262 «Спецификация языка ECMAScript®», гласит:

В случае если в объекте есть повторяющиеся строки имени, лексически предшествующие значения для того же ключа должны быть перезаписаны.

Другими словами, последнее значение выигрывает.


Попытка синтаксического анализа строки с дублированными именами в реализации Java Дугласа Крокфорда (создателя JSON) приводит к исключению :

org.json.JSONException: Duplicate key "status"  at
org.json.JSONObject.putOnce(JSONObject.java:1076)
user454322
источник
1
Предполагается, что JSON является допустимым javascript, поэтому важно проверить, являются ли дублирующиеся ключи действительными JS в литералах. V8, кажется, принимает их: d8 -e 'x={"a":1,"a":2}; print(x.a);'это печатает 2.
Бен Кроуэлл
5
Также спецификация ECMA-262JSON.parse() явно говорит In the case where there are duplicate name Strings within an object, lexically preceding values for the same key shall be overwritten.(другими словами, last-value-wins).
Сергей
4
@BenCrowell: Насколько мне известно, JSON не должен быть допустимым JavaScript, и есть случаи, когда это не так, см. Timelessrepo.com/json-isnt-a-javascript-subset . С учетом сказанного, это, конечно, в значительной степени вдохновлено JavaScript (это даже сказано в спецификации JSON).
Simon Touchtech
2
Стоит отметить, что при использовании javascript «строгого режима», если есть два идентичных ключа, Chrome будет использовать вторую пару ключ-значение и игнорировать первую. IE11 выдаст исключение.
Шахар
18

Есть 2 документа, определяющих формат JSON:

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

Принятые ответы цитаты из 1-го документа. Я думаю, что первый документ более понятен, но второй содержит больше деталей.

Второй документ гласит:

  1. Объекты

    Структура объекта представляется в виде пары фигурных скобок, окружающих ноль или более пар имя / значение (или членов). Имя - это строка. После каждого имени стоит двоеточие, отделяющее имя от значения. Одна запятая отделяет значение от следующего имени. Имена внутри объекта ДОЛЖНЫ быть уникальными.

Так что не запрещено иметь повторяющееся имя, но это не рекомендуется.

toongeorges
источник
10

Я сталкивался с подобным вопросом, когда имел дело с API, который принимает и XML, и JSON, но не документирует, как он будет обрабатывать то, что вы ожидаете от дублированных ключей в принятом JSON.

Ниже приведено правильное XML-представление вашего образца JSON:

<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
неупорядоченное подразумевает уникальность? Я думаю, что сет - это оперативное слово здесь
Патрик Гоули
8
Неупорядоченная коллекция цветов: синий, зеленый, зеленый, синий, красный, синий, зеленый - имеет дубликаты.
Тимоти Шилдс
5
Текстовая фраза, которую вы цитируете: «Объект - это неупорядоченный набор пар имя / значение», не появляется в спецификации JSON ...
Тимоти Шилдс
6
Теперь я вижу, что вы цитировали json.org. Это «близко» к официальному, но это не спецификация. В верхней части страницы есть ссылка на спецификацию, которая дословно повторяется на json.org. При поиске в спецификации слово «неупорядоченный» не появляется, а слово «набор» появляется только в контекстах, не связанных с объектами JSON.
Тимоти Шилдс
5
Обратите внимание, что он говорит «неупорядоченный набор пар имя / значение », а не имена. То есть, { (a,b), (a,c) } это уникальный набор. Так что технически под определением json.org {"a":1,"a":2}действует, но {"a":1,"a":2,"a":1}это не так. Также обратите внимание, что ECMA-404 (действительный стандарт) избегает использования слова «набор»:An object structure is represented as a pair of curly bracket tokens surrounding zero or more name/value pairs.
Сергей
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 и, например, API MongoDB полагаются на упорядочение ключей в словарях, поэтому то, что вы предлагаете (упорядоченные словари), не случайно. Вам просто нужен специализированный парсер.
Бинки
2

Отвечая на цель, есть разные ответы:

Используя JSON для сериализации объектов (JavaScriptObjectNotation), каждый элемент словаря сопоставляется с индивидуальным свойством объекта, поэтому разные записи, определяющие значение для одного и того же свойства, не имеют смысла.

Тем не менее, я столкнулся с тем же вопросом из очень специфического варианта использования: при написании примеров JSON для тестирования API мне было интересно, как добавить комментарии в наш файл JSON, не нарушая удобство использования. Спецификация JSON не знает комментариев, поэтому я предложил очень простой подход:

Использовать дубликаты ключей для комментирования наших примеров JSON . Пример:

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

Сериализаторы JSON, которые мы используем, не имеют проблем с этими дубликатами «REMARK», и код нашего приложения просто игнорирует эти небольшие накладные расходы.

Таким образом, несмотря на то, что на прикладном уровне нет никакого смысла, эти дубликаты для нас предоставляют ценный обходной путь, позволяющий добавлять комментарии к нашим образцам тестирования, не нарушая удобство использования JSON.

aknoepfel
источник
Это плохая идея. Включая дубликаты ключей, даже если вам не нужно читать данные в них, вы полагаетесь на неопределенное поведение. Некоторые парсеры, такие как JSON-java парсер Крокфорда, выдают исключение и отказываются анализировать данные.
Ричард Смит
На самом деле отлично работает в нашей среде, поэтому удовлетворяет мои потребности, хотя я согласен с вами, что это не совсем в порядке;)
aknoepfel
@RichardSmith Я бы сказал, что парсеры и новые спецификации ES определили поведение.
Бинки
2

Отправка и ответ, потому что есть много устаревших идей и путаницы в отношении стандартов. По состоянию на декабрь 2017 года существует два конкурирующих стандарта:

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 (которую я бы лично принял за авторитет), это действительно, точка. Для меня это означает, что любой парсер, который отказывается анализировать его, сломан. Следует хотя бы разобрать согласно обоим этим стандартам. Но то, как он превращается в ваш родной объект выбора, в любом случае, является уникальным ключом или нет, полностью зависит от среды и ситуации, и с самого начала ничего из этого не входит в стандарт.

xthr33
источник
json.org фактически предшествует стандартизации ECMA. Я полагаю, что это было фактически настроено самим Крокфордом (именно поэтому у него есть бесстыдная заглушка для его книги). На тот момент это был авторитет для JSON.
максимум
1

Это не определено в стандарте ECMA JSON . И вообще говоря, отсутствие определения в стандарте означает: «Не рассчитывайте, что это работает одинаково везде».

Если вы азартный игрок, «многие» движки JSON разрешат дублирование и просто используют последнее указанное значение. Это:

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

Становится так:

Object {a: 3, b: 2}

Но если вы не игрок, не рассчитывайте на это!

svidgen
источник
1

Стандарт говорит так:

Языки программирования сильно различаются в зависимости от того, поддерживают ли они объекты, и если да, то какие характеристики и ограничения предлагают объекты. Модели объектных систем могут сильно отличаться друг от друга и продолжают развиваться. Вместо этого 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);
}
Джон Карлсон
источник
1
Это не ошибка, и в разделе, который вы цитировали, объясняется, почему это не так: разные языки ведут себя по-разному, и анализаторы JSON будут делать то, что наиболее естественно для этого языка. В любом случае, этот ответ не добавляет ничего такого, что пользователь 454322 еще не сказал выше.
Ричард Смит
1

Согласно RFC-7159, действующему стандарту для JSON, опубликованному Инженерной рабочей группой по Интернету (IETF), говорится: «Имена внутри объекта ДОЛЖНЫ быть уникальными». Однако, согласно RFC-2119, который определяет терминологию, используемую в документах IETF, слово «должен» фактически означает «... могут существовать веские причины в определенных обстоятельствах игнорировать конкретный элемент, но следует понимать все последствия и тщательно взвесить, прежде чем выбрать другой курс ". По сути это означает, что, хотя рекомендуется иметь уникальные ключи, это не обязательно. Мы можем иметь дубликаты ключей в объекте JSON, и он все равно будет действительным.

Из практического применения я видел, что значение из последнего ключа рассматривается, когда дубликаты ключей находятся в JSON.

Navaneetha
источник
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