A sintaxe JSON permite chaves duplicadas em um objeto?


205

Este json é válido?

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

http://jsonlint.com/ diz que sim.

http://www.json.org/ não diz nada sobre isso ser proibido.

Mas, obviamente, não faz muito sentido, faz? A maioria das implementações provavelmente usa uma hashtable, portanto ela está sendo substituída de qualquer maneira.


1
C # 's Json.NET remove o primeiro par de chaves se você deserialise a umDictionary<string, string>
Sam Leach

No caso de alguém chega aqui esperando por uma solução para encontrar valores duplicados em cordas JSON, confira o json validador online gratuito
Pepijn Olivier

jsonlint.com diz que sim. isso não acontecer, ele remove todos, mas o último par de valores-chave e, em seguida, valida-lo, o que torna válida
Tim

7
Então o padrão é quebrado
Brad Thomas

1
Eu usei o nome da chave "-" como comentarista e o valor é uma única linha de string como comentário. Então, espero que nenhum analisador se queixe.
Lothar

Respostas:


126

A partir do padrão (p. Ii) :

Espera-se que outros padrões se refiram a esse, aderindo estritamente ao formato de texto JSON, enquanto impõem restrições a vários detalhes de codificação. Tais padrões podem exigir comportamentos específicos. O próprio JSON não especifica nenhum comportamento.

Mais abaixo no padrão (p. 2), a especificação para um objeto JSON:

Uma estrutura de objeto é representada como um par de tokens de colchete envolvendo zero ou mais pares de nome / valor. Um nome é uma string. Um token de dois pontos segue cada nome, separando o nome do valor. Um único token de vírgula separa um valor do nome a seguir.

Diagrama para Objeto JSON

Ele não faz nenhuma menção de que chaves duplicadas sejam inválidas ou válidas; portanto, de acordo com a especificação, eu assumiria com segurança que isso significa que elas são permitidas.

O fato de a maioria das implementações de bibliotecas JSON não aceitar chaves duplicadas não entra em conflito com o padrão, devido à primeira cotação.

Aqui estão dois exemplos relacionados à biblioteca padrão C ++. Ao desserializar algum objeto JSON em um std::map, faria sentido recusar chaves duplicadas. Mas, ao desserializar algum objeto JSON em um std::multimap, faria sentido aceitar chaves duplicadas normalmente.


5
Eu acho que posso aceitar isso como uma resposta, embora eu goste da menção de @PatrickGoley que no json.org ele é chamado de um conjunto de pares chave / valor que implica exclusividade, o que significa que não é válido.
clamp

8
@clamp json.org não é o padrão e, até onde eu sei, não é administrado pela Emca International. json.org parece apresentado anonimamente. Esta é a especificação: ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf O que diz no json.org não é relevante.
Timothy Shields

5
@clamp Considere o std::multimapexemplo que acabei de adicionar. Pode ser serializado como um objeto JSON com chaves potencialmente duplicadas.
Timothy Shields

1
O @clamp sendo um conjunto de pares de chave / valor não exclui nomes duplicados. {"a":1,"a":2}é um conjunto de dois pares distintos de chave / valor. De fato, até {"a":1,"a":1}poderia ser pensado como um conjunto de pares de chave / valor que, por acaso, possui apenas um elemento. O fato de ser repetido pode ser pensado apenas como uma peculiaridade sintática. Uma definição melhor seria: "Um objeto é uma função parcial de cadeias (nomes) para valores".
Marcelo Cantos

2
@TimothyShields E esse padrão ao qual você vinculou diz: "A sintaxe JSON não impõe nenhuma restrição às cadeias de caracteres usadas como nomes, não exige que as cadeias de nomes sejam únicas e não atribui nenhum significado à ordem dos pares nome / valor"
Charles

135

A resposta curta: Sim, mas não é recomendado.
A resposta longa: depende do que você chama de válido ...


ECMA-404 "A sintaxe JSON Data Interchange" não diz nada sobre nomes duplicados (chaves).


No entanto, o RFC 8259 "O formato de intercâmbio de dados da JavaScript Object Notation (JSON)" diz:

Os nomes dentro de um objeto devem ser únicos.

Neste contexto, DEVE ser entendido como especificado no BCP 14 :

DEVE Esta palavra, ou o adjetivo "RECOMENDADO", significa que podem existir razões válidas em circunstâncias particulares para ignorar um item em particular, mas todas as implicações devem ser entendidas e ponderadas cuidadosamente antes de escolher um curso diferente.


A RFC 8259 explica por que nomes únicos (chaves) são bons:

Um objeto cujos nomes são todos únicos é interoperável no sentido de que todas as implementações de software que recebem esse objeto concordam com os mapeamentos de valor de nome. Quando os nomes dentro de um objeto não são exclusivos, o comportamento do software que recebe esse objeto é imprevisível. Muitas implementações relatam apenas o par sobrenome / valor. Outras implementações relatam um erro ou falha ao analisar o objeto, e algumas implementações relatam todos os pares de nome / valor, incluindo duplicatas.



Além disso, como Serguei apontou nos comentários: ECMA-262 "ECMAScript® Language Specification", diz:

No caso de existirem cadeias de caracteres duplicadas em um objeto, os valores lexicamente precedentes para a mesma chave serão substituídos.

Em outras palavras, o último valor vence.


Tentar analisar uma sequência com nomes duplicados com a implementação Java por Douglas Crockford (o criador do JSON) resulta em uma exceção :

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

1
O JSON deve ser um javascript válido, portanto, é relevante verificar se chaves duplicadas são JS válidas em literais. O V8 parece aceitá-los: d8 -e 'x={"a":1,"a":2}; print(x.a);'Isso imprime 2. #
Ben Crowell 10/10

5
Além disso, a especificação do ECMA-262 para JSON.parse()explicitamente diz In the case where there are duplicate name Strings within an object, lexically preceding values for the same key shall be overwritten.(em outras palavras, o último valor ganha).
Serguei

4
@BenCrowell: Até onde eu sei, o JSON não deve ser JavaScript válido e há casos em que não é, consulte timelessrepo.com/json-isnt-a-javascript-subset . Com isso dito, é claro que foi fortemente inspirado pelo JavaScript (o mesmo diz na especificação JSON).
Simon Touchtech 17/08/16

2
Vale a pena notar que, ao usar o "modo estrito" do javascript, se houver duas chaves idênticas, o chrome usará o segundo par de valores-chave e ignorará o primeiro. O IE11 lançará uma exceção.
Shahar

18

Existem 2 documentos especificando o formato JSON:

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

A resposta aceita cita o primeiro documento. Eu acho que o primeiro documento é mais claro, mas o segundo contém mais detalhes.

O segundo documento diz:

  1. Objetos

    Uma estrutura de objeto é representada como um par de colchetes envolvendo zero ou mais pares de nome / valor (ou membros). Um nome é uma string. Um único dois pontos vem após cada nome, separando o nome do valor. Uma única vírgula separa um valor do seguinte nome. Os nomes dentro de um objeto devem ser únicos.

Portanto, não é proibido ter um nome duplicado, mas é desencorajado.


10

Me deparei com uma pergunta semelhante ao lidar com uma API que aceita XML e JSON, mas não documenta como ela lidaria com o que você esperaria que fossem chaves duplicadas no JSON aceito.

A seguir, é uma representação XML válida do seu JSON de amostra:

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

Quando isso é convertido em JSON, você obtém o seguinte:

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

Um mapeamento natural de um idioma que lida com o que você pode chamar de chaves duplicadas para outro pode servir como uma referência potencial de boas práticas aqui.

Espero que ajude alguém!


5

A especificação JSON diz o seguinte:

Um objeto é um conjunto não ordenado de pares nome / valor.

A parte importante aqui é "não ordenada": implica exclusividade de chaves, porque a única coisa que você pode usar para se referir a um par específico é a chave.

Além disso, a maioria das bibliotecas JSON desserializa objetos JSON para mapas / dicionários de hash, onde as chaves são garantidas como únicas. O que acontece quando você desserializa um objeto JSON com chaves duplicadas depende da biblioteca: na maioria dos casos, você receberá um erro ou apenas o último valor de cada chave duplicada será levado em consideração.

Por exemplo, em Python, json.loads('{"a": 1, "a": 2}')retorna {"a": 2}.


23
desordenado implica singularidade? Acho conjunto é a palavra-chave aqui
Patrick Goley

8
Uma coleção desordenada de cores: azul, verde, verde, azul, vermelho, azul, verde - possui duplicatas.
Timothy Shields

5
A frase texto que você está citando, "Um objeto é um conjunto não ordenado de pares nome / valor," não aparece na especificação JSON ...
Timothy Shields

6
Vejo agora que você estava citando json.org. É "perto" do oficial, mas não é a especificação. O topo da página possui um link para a especificação, repetida literalmente em json.org. Se você pesquisar a especificação, a palavra "não ordenado" não aparecerá e a palavra "conjunto" aparecerá apenas em contextos não relacionados aos objetos JSON.
Timothy Shields

5
Observe que diz "conjunto não ordenado de pares nome / valor ", pares não nomes. Ou seja, { (a,b), (a,c) } é um conjunto único. Portanto, tecnicamente, na definição do json.org, {"a":1,"a":2}é válido, mas {"a":1,"a":2,"a":1}não é. Observe também que o ECMA-404 (o padrão atual) evita o uso da palavra "set":An object structure is represented as a pair of curly bracket tokens surrounding zero or more name/value pairs.
Serguei

3

DEVE ser único, não significa que DEVE ser único. No entanto, como afirmado, alguns analisadores falhariam e outros apenas usariam o último valor analisado. No entanto, se a especificação foi limpa um pouco para permitir duplicatas, eu pude ver um uso em que você pode ter um manipulador de eventos que está transformando o JSON em HTML ou em algum outro formato ... nesses casos, seria perfeitamente válido analisar o JSON e criar outro formato de documento ...

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

poderia então analisar facilmente o html, por exemplo

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

Eu posso ver o raciocínio por trás da pergunta, mas como está ... eu não confiaria nela.


A matriz do seu primeiro exemplo está sem vírgula. Além disso, é inconsistente consigo mesmo. Se você usar um dicionário como uma coleção ordenada, sua matriz externa também deve ser apenas um objeto. Ou seja {"div":{"p":"hello","p":"universe"}, "div":{"h1":"Heading 1","p":"another paragraph"}},. Agora, muitas pessoas e estruturas tratam objetos JSON como dicionários não ordenados, mas o JavaScript e a API do MongoDB, por exemplo, dependem da ordem das chaves nos dicionários; portanto, o que você está sugerindo (dicionários ordenados) não é inédito. Você só precisa de um analisador especializado.
binki

2

Solicitando um propósito, há respostas diferentes:

Usando JSON para serializar objetos (JavaScriptObjectNotation), cada elemento do dicionário é mapeado para uma propriedade de objeto individual, portanto, entradas diferentes que definem um valor para a mesma propriedade não têm significado.

No entanto, encontrei a mesma pergunta em um caso de uso muito específico: escrevendo amostras JSON para testes de API, estava pensando em como adicionar comentários ao nosso arquivo JSON sem interromper a usabilidade. A especificação JSON não conhece comentários, então criei uma abordagem muito simples:

Para usar chaves duplicadas para comentar nossas amostras JSON . Exemplo:

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

Os serializadores JSON que estamos usando não têm problemas com essas duplicatas "REMARK" e nosso código de aplicativo simplesmente ignora essa pequena sobrecarga.

Portanto, mesmo que não haja significado na camada de aplicativo, essas duplicatas fornecem uma solução valiosa para adicionar comentários às nossas amostras de teste sem interromper a usabilidade do JSON.


Esta é uma má ideia. Ao incluir chaves duplicadas, mesmo que você não precise ler os dados nelas, você depende de um comportamento indefinido. Alguns analisadores, como o analisador JSON-java de Crockford, lançam uma exceção e se recusam a analisar os dados.
Richard Smith

Na verdade, estou funcionando perfeitamente em nosso ambiente, atendendo às minhas necessidades, mesmo que eu concorde com você que é algum tipo de especificação;)
aknoepfel

@ RichardSmith Eu diria que os analisadores e as especificações mais recentes do ES definiram o comportamento.
binki

2

Postagem e resposta, porque há muitas idéias desatualizadas e confusão sobre os padrões. Em dezembro de 2017, havia dois padrões concorrentes:

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

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

O json.org sugere que o ECMA-404 é o padrão, mas este site não parece ser uma autoridade. Embora eu ache justo considerar a ECMA a autoridade, o importante aqui é que a única diferença entre os padrões (em relação às chaves exclusivas) é que a RFC 8259 diz que as chaves devem ser únicas e o ECMA-404 diz que elas não são necessárias ser único.

RFC-8259:

"Os nomes dentro de um objeto DEVEM ser únicos."

A palavra "should" em todas as maiúsculas como essa tem um significado no mundo da RFC, definido especificamente em outro padrão (BCP 14, RFC 2119 - https://tools.ietf.org/html/rfc2119 ) como,

  1. DEVE Esta palavra, ou o adjetivo "RECOMENDADO", significa que podem existir razões válidas em circunstâncias particulares para ignorar um item em particular, mas todas as implicações devem ser entendidas e ponderadas cuidadosamente antes de escolher um curso diferente.

ECMA-404:

"A sintaxe JSON não impõe restrições às cadeias usadas como nomes, não exige que as cadeias de nomes sejam exclusivas e não atribui qualquer significado à ordem dos pares nome / valor."

Portanto, não importa como você o corta, é JSON sintaticamente válido .

O motivo apresentado para a recomendação chave exclusiva na RFC 8259 é:

Um objeto cujos nomes são todos únicos é interoperável no sentido de que todas as implementações de software que recebem esse objeto concordam com os mapeamentos de valor de nome. Quando os nomes dentro de um objeto não são exclusivos, o comportamento do software que recebe esse objeto é imprevisível. Muitas implementações relatam apenas o par sobrenome / valor. Outras implementações relatam um erro ou falha ao analisar o objeto, e algumas implementações relatam todos os pares de nome / valor, incluindo duplicatas.

Em outras palavras, do ponto de vista da RFC 8259, é válido, mas seu analisador pode vomitar e não há promessa de qual valor, se houver, será emparelhado com essa chave. Do ponto de vista da ECMA-404 (que eu pessoalmente consideraria a autoridade), é válido, ponto final. Para mim, isso significa que qualquer analisador que se recusa a analisá-lo está quebrado. Deve pelo menos analisar de acordo com esses dois padrões. Mas como ele se transforma no seu objeto de escolha nativo é, em qualquer caso, chaves únicas ou não, completamente dependente do ambiente e da situação, e nada disso está no padrão para começar.


O json.org é anterior à padronização da ECMA. Acredito que foi realmente configurado pelo próprio Crockford (e é por isso que ele tem um plug descarado para o livro). Nesse ponto, era a autoridade para o JSON.
Max

1

Não está definido no padrão ECMA JSON . E de um modo geral, a falta de definição em um padrão significa: "Não conte com isso funcionando da mesma maneira em todos os lugares".

Se você é um jogador, "muitos" mecanismos JSON permitirão duplicação e simplesmente usarão o último valor especificado. Este:

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

Torna-se isso:

Object {a: 3, b: 2}

Mas se você não é um jogador, não conte com isso!


1

O padrão diz o seguinte:

As linguagens de programação variam amplamente se suportam objetos e, em caso afirmativo, quais características e restrições os objetos oferecem. Os modelos de sistemas de objetos podem ser bastante divergentes e continuam a evoluir. Em vez disso, o JSON fornece uma notação simples para expressar coleções de pares nome / valor. A maioria das linguagens de programação possui algum recurso para representar essas coleções, que podem usar nomes como registro, estrutura, ditado, mapa, hash ou objeto.

O bug está no node.js pelo menos. Esse código é bem-sucedido no node.js.

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

1
Não é um bug, e a seção citada explica por que não é: idiomas diferentes se comportam de maneira diferente, e os analisadores JSON farão o que é mais natural para esse idioma. De qualquer forma, esta resposta não adiciona nada que o usuário454322 ainda não tenha dito acima.
Richard Smith

1

De acordo com a RFC-7159, o padrão atual para JSON publicado pela Internet Engineering Task Force (IETF), declara "Os nomes dentro de um objeto DEVEM ser únicos". No entanto, de acordo com a RFC-2119, que define a terminologia usada nos documentos da IETF, a palavra "deveria" de fato significa "... pode haver razões válidas em circunstâncias particulares para ignorar um item em particular, mas todas as implicações devem ser entendidas e cuidadosamente pesado antes de escolher um curso diferente ". O que isso significa essencialmente é que, embora seja recomendável ter chaves exclusivas, não é obrigatório. Podemos ter chaves duplicadas em um objeto JSON, e isso ainda seria válido.

A partir da aplicação prática, vi que o valor da última chave é considerado quando chaves duplicadas são encontradas em um JSON.


0

Em C #, se você desserializar para a Dictionary<string, string>, leva o último par de valores-chave:

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

se você tentar desserializar para

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

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

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

você recebe uma Newtonsoft.Json.JsonSerializationExceptionexceção.


2
Eu acho que é o que a maioria das implementações (se não todas) faz, mas ela não responde à minha pergunta se é válida a partir da especificação do JSON.
clamp

@svidgen: Essa nem é a implementação da Microsoft ... é uma biblioteca de terceiros.
BoltClock

@BoltClock Ah, toque.
svidgen
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.