Чи дозволяє синтаксис JSON дублювати ключі в об’єкті?


205

Це дійсний json?

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

http://jsonlint.com/ каже так.

http://www.json.org/ нічого не говорить про те, що це заборонено.

Але очевидно, це не має великого сенсу, чи не так? Більшість реалізацій, ймовірно, використовують хешбел, тому його в будь-якому випадку переосмислюють.


1
C # 's Json.NET видаляє першу пару ключів, якщо ви скасуєте номер наDictionary<string, string>
Сем Ліч,

Якщо хтось сюди приїде сподіваючись на рішення знайти дублікати значень у рядках JSON, перегляньте безкоштовний онлайн-валідатор json
Pepijn Olivier

jsonlint.com каже, що так. це не так, він видаляє всі, крім останньої пари ключ-значення, а потім підтверджує це, що робить його дійсним
Тім

7
Тоді стандарт порушується
Бред Томас

1
Я використовував ім'я ключа "-" як коментатор, а значення - це рядок рядка як коментар. Тож я сподіваюся, що жоден аналізатор не поскаржиться на це.
Лотар

Відповіді:


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}можна розглядати як сукупність пар ключів / значень, у яких, мабуть, є лише один елемент. Те, що воно повторюється, можна вважати лише синтаксичним приводом. Кращим визначенням було б: "Об'єкт - це часткова функція від рядків (імен) до значень".
Марсело Кантос

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)

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.(іншими словами, виграє last-value).
Сергей

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. Об'єкти

    Структура об'єкта представлена ​​у вигляді пари фігурних дужок, що оточують нуль або більше пар імен / значень (або членів). Ім'я - це рядок. Одинарна двокрапка надходить після кожного імені, відокремлюючи ім'я від значення. Одинарна кома відокремлює значення від наступного імені. Імена в межах об’єкта ДОЛЖНІ бути унікальними.

Тож не забороняється мати дублюючу назву, але це перешкоджає.


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 (фактичний стандарт) уникає використання слова "set":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 покладаються на впорядкування ключів у словниках, тому те, що ви пропонуєте (упорядковані словники), не є нечуваним. Вам просто знадобиться спеціалізований аналізатор.
binki

2

Що стосується мети, є різні відповіді:

Використовуючи JSON для серіалізації об’єктів (JavaScriptObjectNotation), кожен елемент словника відображає на властивість індивідуального об'єкта, тому різні записи, що визначають значення для тієї ж властивості, не мають значення.

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

Для використання дублюючих ключів для коментування наших зразків JSON . Приклад:

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

Серіалізатори JSON, якими ми користуємось, не мають проблем із цими дублікатами «ЗАПАМНИТЬ», і наш код програми просто ігнорує цю невелику накладну вартість.

Тож, хоча на шарі додатків немає сенсу, ці копії для нас дають цінне рішення для додавання коментарів до наших тестових зразків, не порушуючи зручності використання JSON.


Це погана ідея. Включаючи повторювані ключі, навіть якщо вам не потрібно читати в них дані, ви покладаєтесь на не визначену поведінку. Деякі аналізатори, наприклад, аналізатор JSON-java Crockford, кидають виняток і відмовляються аналізувати дані.
Річард Сміт

Насправді прекрасно працює в нашому середовищі, тому обслуговуючи мої потреби, хоча я згоден з вами, що це якась специфіка;)
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 (яку я особисто вважаю владою), це термін дії. Для мене це означає, що будь-який аналізатор, який відмовляється розібрати його, порушений. Він повинен принаймні проаналізувати обидва ці стандарти. Але те, як воно перетворюється на ваш рідний об’єкт вибору, в будь-якому випадку є унікальними ключами чи ні, повністю залежить від оточення та ситуації, і нічого з цього не є стандартним для початку.


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-аналізатори будуть робити те, що є найбільш природним для цієї мови. У будь-якому випадку ця відповідь не додає нічого, про що користувач454322 вже не сказав вище.
Річард Сміт

1

Відповідно до RFC-7159, чинний стандарт для JSON, опублікований Інженерною робочою групою (IETF), зазначає, що "Імена в об'єкті повинні бути унікальними". Однак, згідно з RFC-2119, який визначає термінологію, що використовується в документах IETF, слово "повинне" насправді означає "... можуть існувати вагомі причини, в яких обставинах ігнорувати певний предмет, але слід розуміти повний вплив ретельно зваживши, перш ніж вибрати інший курс ". Це по суті означає, що, хоча мати унікальні ключі, рекомендується, це не обов'язково. У об’єкта 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.