La sintassi JSON consente chiavi duplicate in un oggetto?


205

Questo json è valido?

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

http://jsonlint.com/ dice sì.

http://www.json.org/ non dice nulla in merito al fatto che è proibito.

Ma ovviamente non ha molto senso, vero? La maggior parte delle implementazioni utilizza probabilmente una tabella hash, quindi viene comunque ignorata.


1
Json.NET di C # rimuove la prima coppia di chiavi se si deserializza unDictionary<string, string>
Sam Leach,

Se qualcuno arriva qui sperando in una soluzione per trovare valori duplicati nelle stringhe JSON, controlla il validatore json online gratuito
Pepijn Olivier,

jsonlint.com dice di si. non lo fa, rimuove tutto tranne l'ultima coppia chiave-valore e quindi lo convalida, il che lo rende valido
Tim

7
Quindi lo standard è rotto
Brad Thomas il

1
Ho usato il nome chiave "-" come commentatore e il valore è una singola riga di stringa come commento. Quindi spero che nessun parser se ne lamenterà.
Lothar,

Risposte:


126

Dallo standard (p. Ii) :

Si prevede che altri standard faranno riferimento a questo, aderendo rigorosamente al formato di testo JSON, imponendo al contempo restrizioni su vari dettagli di codifica. Tali standard possono richiedere comportamenti specifici. JSON stesso non specifica alcun comportamento.

Più in basso nello standard (p. 2), la specifica per un oggetto JSON:

Una struttura di oggetti è rappresentata come una coppia di token parentesi graffa che circonda zero o più coppie nome / valore. Un nome è una stringa. Un singolo token di due punti segue ogni nome, separando il nome dal valore. Un singolo token virgola separa un valore dal seguente nome.

Diagramma per l'oggetto JSON

Non fa alcuna menzione del fatto che le chiavi duplicate non siano valide o valide, quindi in base alle specifiche presumo che significhi che sono consentite.

Il fatto che la maggior parte delle implementazioni delle librerie JSON non accetta chiavi duplicate non è in conflitto con lo standard, a causa della prima citazione.

Ecco due esempi relativi alla libreria standard C ++. Quando si deserializza un oggetto JSON in un std::mapsenso, avrebbe senso rifiutare chiavi duplicate. Ma quando si deserializza un oggetto JSON in un std::multimapsenso, avrebbe senso accettare chiavi duplicate normalmente.


5
immagino di poter accettare questa come una risposta, anche se mi piace la menzione di @PatrickGoley che su json.org è chiamato un insieme di coppie chiave / valore che implica unicità che significherebbe che non è valido.
morsetto

8
@clamp json.org non è lo standard e, per quanto ne so, non è gestito da Emca International. json.org sembra presentato in modo anonimo. Questa è la specifica: ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf Ciò che dice su json.org non è rilevante.
Timothy Shields,

5
@clamp Considera l' std::multimapesempio che ho appena aggiunto. Può essere serializzato come oggetto JSON con chiavi potenzialmente duplicate.
Timothy Shields,

1
@clamp essendo un insieme di coppie chiave / valore non preclude i nomi duplicati. {"a":1,"a":2}è un insieme di due coppie chiave / valore distinte. In effetti, persino {"a":1,"a":1}potrebbe essere pensato come un insieme di coppie chiave / valore che sembra avere un solo elemento. Il fatto che sia ripetuto può essere considerato solo una stranezza sintattica. Una definizione migliore sarebbe "Un oggetto è una funzione parziale dalle stringhe (nomi) ai valori".
Marcelo Cantos,

2
@TimothyShields E lo standard a cui ti sei collegato dice: "La sintassi JSON non impone alcuna restrizione sulle stringhe utilizzate come nomi, non richiede che le stringhe di nomi siano univoche e non attribuisce alcun significato all'ordinamento delle coppie nome / valore"
Charles,

135

La risposta breve: Sì, ma non è raccomandato.
La risposta lunga: dipende da ciò che chiami valido ...


ECMA-404 "La sintassi di interscambio dati JSON" non dice nulla sui nomi duplicati (chiavi).


Tuttavia, RFC 8259 "Il formato di interscambio di dati JSON (JavaScript Object Notation)" dice:

I nomi all'interno di un oggetto DOVREBBERO essere univoci.

In questo contesto DOVREBBE essere compreso come specificato nel BCP 14 :

DOVREBBE Questa parola, o l'aggettivo "RACCOMANDATO", significa che possono esistere ragioni valide in circostanze particolari per ignorare un elemento particolare, ma le implicazioni complete devono essere comprese e ponderate attentamente prima di scegliere un corso diverso.


RFC 8259 spiega perché i nomi univoci (chiavi) sono buoni:

Un oggetto i cui nomi sono tutti univoci è interoperabile, nel senso che tutte le implementazioni software che ricevono quell'oggetto saranno d'accordo sulle mappature nome-valore. Quando i nomi all'interno di un oggetto non sono univoci, il comportamento del software che riceve tale oggetto è imprevedibile. Molte implementazioni riportano solo la coppia cognome / valore. Altre implementazioni riportano un errore o non riescono ad analizzare l'oggetto e alcune implementazioni riportano tutte le coppie nome / valore, inclusi i duplicati.



Inoltre, come sottolineato da Serguei nei commenti: ECMA-262 "ECMAScript® Language Specification", recita:

Nel caso in cui vi siano stringhe di nomi duplicati all'interno di un oggetto, i valori che precedono lessicamente per la stessa chiave devono essere sovrascritti.

In altre parole, vince l'ultimo valore.


Cercare di analizzare una stringa con nomi duplicati con l' implementazione Java di Douglas Crockford (il creatore di JSON) genera un'eccezione :

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

1
JSON dovrebbe essere javascript valido, quindi è importante verificare se le chiavi duplicate sono JS valide in termini letterali. V8 sembra accettarli: d8 -e 'x={"a":1,"a":2}; print(x.a);'questo stampa 2.
Ben Crowell,

5
Anche l' ECMA-262 spec per JSON.parse()esplicitamente dice In the case where there are duplicate name Strings within an object, lexically preceding values for the same key shall be overwritten.(in altre parole ultimo valore di vittorie).
Serguei,

4
@BenCrowell: per quanto ne so, JSON non dovrebbe essere JavaScript valido e ci sono casi in cui non lo è, vedi timelessrepo.com/json-isnt-a-javascript-subset . Detto questo, è stato ovviamente fortemente ispirato da JavaScript (lo dice anche nelle specifiche JSON).
Simon Touchtech,

2
Vale la pena notare che quando si utilizza javascript "modalità rigorosa", se ci sono due chiavi identiche Chrome utilizzerà la seconda coppia chiave-valore e ignorerà la prima. IE11 genererà un'eccezione.
Shahar,

18

Esistono 2 documenti che specificano il formato JSON:

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

La risposta accettata cita dal 1o documento. Penso che il primo documento sia più chiaro, ma il secondo contiene più dettagli.

Il secondo documento dice:

  1. Oggetti

    Una struttura di oggetti è rappresentata come una coppia di parentesi graffe che circondano zero o più coppie nome / valore (o membri). Un nome è una stringa. Un singolo punto viene dopo ogni nome, separando il nome dal valore. Una singola virgola separa un valore dal seguente nome. I nomi all'interno di un oggetto DOVREBBERO essere univoci.

Quindi non è vietato avere un nome duplicato, ma è scoraggiato.


10

Mi sono imbattuto in una domanda simile quando ho a che fare con un'API che accetta sia XML che JSON, ma non documenta come gestirà quelle che ci si aspetterebbe fossero chiavi duplicate nell'accettazione JSON.

Di seguito è riportata una rappresentazione XML valida del JSON di esempio:

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

Quando questo viene convertito in JSON, si ottiene quanto segue:

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

Una mappatura naturale da una lingua che gestisce quelle che potresti chiamare chiavi duplicate a un'altra, può servire come potenziale riferimento alle migliori pratiche qui.

Spero che aiuti qualcuno!


5

Le specifiche JSON dicono questo:

Un oggetto è un insieme non ordinato di coppie nome / valore.

La parte importante qui è "non ordinata": implica l'unicità delle chiavi, perché l'unica cosa che puoi usare per fare riferimento a una coppia specifica è la sua chiave.

Inoltre, la maggior parte delle librerie JSON deserializza gli oggetti JSON in mappe / dizionari hash, dove le chiavi sono garantite uniche. Cosa succede quando si deserializza un oggetto JSON con chiavi duplicate dipende dalla libreria: nella maggior parte dei casi, si riceverà un errore o verrà preso in considerazione solo l'ultimo valore per ogni chiave duplicata.

Ad esempio, in Python, json.loads('{"a": 1, "a": 2}')restituisce {"a": 2}.


23
il non ordinato implica unicità? Penso che set sia la parola
chiave

8
Una collezione di colori non ordinata: blu, verde, verde, blu, rosso, blu, verde - Ha duplicati.
Timothy Shields,

5
La frase di testo che stai citando, "Un oggetto è un insieme non ordinato di coppie nome / valore" non appare nelle specifiche JSON ...
Timothy Shields,

6
Vedo ora che stavi citando json.org. È "vicino" al funzionario, ma non è la specifica. La parte superiore della pagina contiene un collegamento alle specifiche, che viene ripetuto alla lettera su json.org. Se si cerca la specifica, la parola "non ordinata" non viene visualizzata e la parola "set" appare solo in contesti non correlati agli oggetti JSON.
Timothy Shields,

5
Si noti che dice "set non ordinato di coppie nome / valore ", coppie non nomi. Cioè, { (a,b), (a,c) } è un set unico. Quindi tecnicamente sotto la definizione di json.org {"a":1,"a":2}è valido ma {"a":1,"a":2,"a":1}non lo è. Si noti inoltre che ECMA-404 (lo standard attuale) evita di usare la parola "set":An object structure is represented as a pair of curly bracket tokens surrounding zero or more name/value pairs.
Serguei

3

DOVREBBE essere unico non significa che DEVE essere unico. Tuttavia, come affermato, alcuni parser fallirebbero e altri userebbero solo l'ultimo valore analizzato. Tuttavia, se le specifiche sono state ripulite un po 'per consentire i duplicati, allora potrei vedere un uso in cui potresti avere un gestore di eventi che sta trasformando il JSON in HTML o in qualche altro formato ... in questi casi sarebbe perfettamente valido per analizzare JSON e creare un altro formato di documento ...

[
  "div":
  {
    "p":"hello",
    "p":"universe"
  }
  "div":
  {
    "h1":"Heading 1",
    "p":"another paragraph"
  }
]

potrebbe quindi analizzare facilmente HTML, ad esempio

<body>
 <div>
  <p>hello</p>
  <p>universe</p>
 </div>
 <div>
  <h1>Heading 1</h1>
  <p>another paragraph</p>
 </div>
</body>

Riesco a vedere il ragionamento alla base della domanda ma così com'è ... Non mi fiderei.


Nell'array del tuo primo esempio manca una virgola. Inoltre, non è coerente con se stesso. Se utilizzerai un dizionario come raccolta ordinata, anche l'array esterno dovrebbe essere solo un oggetto. Vale a dire, {"div":{"p":"hello","p":"universe"}, "div":{"h1":"Heading 1","p":"another paragraph"}}. Ora, molte persone e framework trattano gli oggetti JSON come dizionari non ordinati, ma JavaScript e, ad esempio, l'API di MongoDB si basano sull'ordinamento delle chiavi all'interno dei dizionari, quindi ciò che stai suggerendo (dizionari ordinati) non è inaudito. Avresti solo bisogno di un parser specializzato.
binki,

2

Chiedere uno scopo, ci sono diverse risposte:

Utilizzando JSON per serializzare gli oggetti (JavaScriptObjectNotation), ciascun elemento del dizionario viene mappato su una proprietà dell'oggetto individuale, quindi le diverse voci che definiscono un valore per la stessa proprietà non hanno alcun significato.

Tuttavia, mi è venuta la stessa domanda da un caso d'uso molto specifico: scrivendo campioni JSON per i test API, mi chiedevo come aggiungere commenti nel nostro file JSON senza interrompere l'usabilità. Le specifiche JSON non conoscono i commenti, quindi ho trovato un approccio molto semplice:

Per utilizzare chiavi duplicate per commentare i nostri esempi JSON . Esempio:

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

I serializzatori JSON che stiamo utilizzando non hanno problemi con questi duplicati "REMARK" e il nostro codice dell'applicazione ignora semplicemente questo piccolo sovraccarico.

Pertanto, anche se non esiste alcun significato a livello di applicazione, questi duplicati forniscono per noi una soluzione alternativa utile per aggiungere commenti ai nostri campioni di test senza compromettere l'usabilità di JSON.


Questa è una cattiva idea. Includendo chiavi duplicate, anche se non è necessario leggere i dati in esse contenuti, si fa affidamento su comportamenti indefiniti. Alcuni parser, come il parser JSON-java di Crockford, generano un'eccezione e si rifiutano di analizzare i dati.
Richard Smith,

In realtà funziona perfettamente nel nostro ambiente, quindi soddisfa i miei bisogni, anche se sono d'accordo con te che è un po 'fuori specifica;)
aknoepfel

@RichardSmith Direi che i parser e le nuove specifiche ES hanno definito il comportamento.
binki,

2

Pubblicare e rispondere perché ci sono molte idee obsolete e confusione sugli standard. A dicembre 2017, ci sono due standard concorrenti:

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

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

json.org suggerisce che ECMA-404 è lo standard, ma questo sito non sembra essere un'autorità. Mentre penso che sia giusto considerare ECMA l'autorità, ciò che è importante qui è, l'unica differenza tra le norme (in materia di chiavi univoche) è che RFC 8259 dice che le chiavi dovrebbero essere unico, e l'ECMA-404 dice che sono non sono necessari per essere unico.

RFC-8259:

"I nomi all'interno di un oggetto DOVREBBERO essere univoci."

La parola "dovrebbe" in maiuscolo, ha un significato all'interno del mondo RFC, che è specificamente definito in un altro standard (BCP 14, RFC 2119 - https://tools.ietf.org/html/rfc2119 ) come,

  1. DOVREBBE Questa parola, o l'aggettivo "RACCOMANDATO", significa che possono esistere ragioni valide in circostanze particolari per ignorare un particolare elemento, ma le implicazioni complete devono essere comprese e ponderate attentamente prima di scegliere un corso diverso.

ECMA-404:

"La sintassi JSON non impone alcuna restrizione alle stringhe utilizzate come nomi, non richiede che le stringhe di nomi siano univoche e non assegna alcun significato all'ordinamento delle coppie nome / valore."

Quindi, indipendentemente da come lo tagli, è JSON sintatticamente valido .

Il motivo fornito per la raccomandazione chiave unica in RFC 8259 è,

Un oggetto i cui nomi sono tutti univoci è interoperabile, nel senso che tutte le implementazioni software che ricevono quell'oggetto saranno d'accordo sulle mappature nome-valore. Quando i nomi all'interno di un oggetto non sono univoci, il comportamento del software che riceve tale oggetto è imprevedibile. Molte implementazioni riportano solo la coppia cognome / valore. Altre implementazioni riportano un errore o non riescono ad analizzare l'oggetto e alcune implementazioni riportano tutte le coppie nome / valore, inclusi i duplicati.

In altre parole, dal punto di vista di RFC 8259, è valido ma il tuo parser potrebbe fallire e non ci sono promesse su quale valore, se presente, verrà associato a quella chiave. Dal punto di vista dell'ECMA-404 (che prenderei personalmente come autorità), è valido, punto. Per me questo significa che qualsiasi parser che rifiuta di analizzarlo è rotto. Dovrebbe almeno analizzare secondo entrambi questi standard. Ma il modo in cui viene trasformato nel tuo oggetto nativo di scelta è, in ogni caso, chiavi uniche o no, completamente dipendente dall'ambiente e dalla situazione, e nulla di tutto ciò è nello standard per cominciare.


json.org in realtà precede la standardizzazione ECMA. Credo che sia stato effettivamente creato dallo stesso Crockford (motivo per cui ha una spina spudorata per il suo libro). A quel punto era l'autorità per JSON.
massimo

1

Non è definito nello standard ECMA JSON . E in generale, una mancanza di definizione in modo standard significa "Non contare su questo lavoro allo stesso modo ovunque".

Se sei un giocatore d'azzardo, "molti" motori JSON consentiranno la duplicazione e utilizzeranno semplicemente l'ultimo valore specificato. Questo:

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

Diventa questo:

Object {a: 3, b: 2}

Ma se non sei un giocatore d'azzardo, non contare su di esso!


1

Lo standard dice questo:

I linguaggi di programmazione variano ampiamente a seconda che supportino gli oggetti e, in tal caso, quali caratteristiche e vincoli offrono gli oggetti. I modelli dei sistemi di oggetti possono essere selvaggiamente divergenti e continuano a evolversi. JSON fornisce invece una semplice notazione per esprimere raccolte di coppie nome / valore. La maggior parte dei linguaggi di programmazione avrà alcune funzionalità per rappresentare tali raccolte, che possono andare con nomi come record, struct, dict, map, hash o object.

Il bug è almeno in node.js. Questo codice ha esito positivo in node.js.

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

1
Non è un bug e la sezione che hai citato spiega perché non lo è: lingue diverse si comportano diversamente e i parser JSON faranno ciò che è più naturale per quella lingua. In ogni caso, questa risposta non aggiunge nulla che l'utente 454322 non abbia già detto sopra.
Richard Smith,

1

Secondo RFC-7159, l'attuale standard per JSON pubblicato da Internet Engineering Task Force (IETF), afferma "I nomi all'interno di un oggetto DOVREBBERO essere univoci". Tuttavia, secondo RFC-2119 che definisce la terminologia utilizzata nei documenti IETF, la parola "dovrebbe" in effetti significa "... possono esistere ragioni valide in circostanze particolari per ignorare un elemento particolare, ma le implicazioni complete devono essere comprese e attentamente valutato prima di scegliere un corso diverso ". Ciò significa essenzialmente che, sebbene si consigliano chiavi univoche, non è un must. Possiamo avere chiavi duplicate in un oggetto JSON e sarebbe comunque valido.

Dall'applicazione pratica, ho visto che il valore dell'ultima chiave viene considerato quando vengono trovate chiavi duplicate in un JSON.


0

In C # se si deserializza a a Dictionary<string, string>, prende l'ultima coppia di valori chiave:

string json = @"{""a"": ""x"", ""a"": ""y""}";
var d = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
// { "a" : "y" }

se provi a deserializzare

class Foo
{
    [JsonProperty("a")]
    public string Bar { get; set; }

    [JsonProperty("a")]
    public string Baz { get; set; }
}

var f = JsonConvert.DeserializeObject<Foo>(json);

ottieni Newtonsoft.Json.JsonSerializationExceptionun'eccezione.


2
suppongo che sia quello che fanno la maggior parte (se non tutte) le implementazioni, ma non risponde alla mia domanda se è valido a partire dalle specifiche di JSON.
morsetto

@svidgen: questa non è nemmeno l'implementazione di Microsoft ... è una libreria di terze parti.
BoltClock

@BoltClock Ah, touche.
svidgen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.