¿La sintaxis JSON permite claves duplicadas en un objeto?

205

¿Es esto válido json?

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

http://jsonlint.com/ dice que sí.

http://www.json.org/ no dice nada acerca de que esté prohibido.

Pero obviamente no tiene mucho sentido, ¿verdad? La mayoría de las implementaciones probablemente usan una tabla hash, por lo que se anula de todos modos.

abrazadera
fuente
1
Json.NET de C # elimina el primer par de claves si deserializa a unDictionary<string, string>
Sam Leach
En caso de que alguien llegue aquí esperando una solución para encontrar valores duplicados en las cadenas JSON, consulte el validador json en línea gratuito
Pepijn Olivier
jsonlint.com dice que sí. no lo hace, elimina todo excepto el último par clave-valor y luego lo valida, lo que lo hace válido
Tim
77
Entonces el estándar se rompe
Brad Thomas
1
Usé el nombre clave "-" como comentador y el valor es una sola línea de cadena como comentario. Así que espero que ningún analizador se queje de eso.
Lothar

Respuestas:

126

Del estándar (p. Ii) :

Se espera que otros estándares se refieran a este, adhiriéndose estrictamente al formato de texto JSON, al tiempo que imponen restricciones en varios detalles de codificación. Tales estándares pueden requerir comportamientos específicos. JSON mismo no especifica ningún comportamiento.

Más abajo en el estándar (p. 2), la especificación para un objeto JSON:

Una estructura de objeto se representa como un par de tokens de llaves que rodean cero o más pares de nombre / valor. Un nombre es una cadena. Un token de dos puntos sigue a cada nombre, separando el nombre del valor. Un solo token de coma separa un valor del siguiente nombre.

Diagrama para objeto JSON

No menciona que las claves duplicadas sean inválidas o válidas, por lo que, de acuerdo con la especificación, asumiría con seguridad que eso significa que están permitidas.

Que la mayoría de las implementaciones de las bibliotecas JSON no aceptan claves duplicadas no entra en conflicto con el estándar, debido a la primera cita.

Aquí hay dos ejemplos relacionados con la biblioteca estándar de C ++. Al deserializar algún objeto JSON en un std::map, tendría sentido rechazar claves duplicadas. Pero cuando se deserializa algún objeto JSON en un std::multimap, tendría sentido aceptar claves duplicadas como de costumbre.

Timothy Shields
fuente
55
Supongo que puedo aceptar esto como respuesta, aunque me gusta la mención de @PatrickGoley de que en json.org se llama un conjunto de pares clave / valor, lo que implica unicidad que significaría que no es válido.
abrazadera
8
@clamp json.org no es el estándar y, por lo que puedo decir, no está dirigido por Emca International. json.org parece presentado de forma anónima. Esta es la especificación: ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf Lo que dice en json.org no es relevante.
Timothy Shields
55
@clamp Considere el std::multimapejemplo que acabo de agregar. Se puede serializar como un objeto JSON con claves potencialmente duplicadas.
Timothy Shields
1
@clamp es un conjunto de pares clave / valor no excluye nombres duplicados. {"a":1,"a":2}es un conjunto de dos pares clave / valor distintos. De hecho, incluso {"a":1,"a":1}podría considerarse como un conjunto de pares clave / valor que tiene un solo elemento. El hecho de que se repita puede considerarse como un capricho sintáctico. Una mejor definición sería: "Un objeto es una función parcial de cadenas (nombres) a valores".
Marcelo Cantos
2
@TimothyShields Y ese estándar al que se vinculó dice: "La sintaxis JSON no impone ninguna restricción en las cadenas utilizadas como nombres, no requiere que las cadenas de nombres sean únicas y no asigna ningún significado al orden de los pares de nombre / valor"
Charles
135

La respuesta corta: sí, pero no es recomendable.
La respuesta larga: depende de lo que llames válido ...


ECMA-404 "La sintaxis de intercambio de datos JSON" no dice nada sobre nombres duplicados (claves).


Sin embargo, RFC 8259 "El formato de intercambio de datos de notación de objetos JavaScript (JSON)" dice:

Los nombres dentro de un objeto DEBEN ser únicos.

En este contexto, DEBE entenderse como se especifica en BCP 14 :

DEBE Esta palabra, o el adjetivo "RECOMENDADO", significa que pueden existir razones válidas en circunstancias particulares para ignorar un artículo en particular, pero todas las consecuencias deben ser comprendidas y sopesar con cuidado antes de elegir un camino diferente.


RFC 8259 explica por qué los nombres únicos (claves) son buenos:

Un objeto cuyos nombres son todos únicos es interoperable en el sentido de que todas las implementaciones de software que reciben ese objeto estarán de acuerdo en las asignaciones de nombre-valor. Cuando los nombres dentro de un objeto no son únicos, el comportamiento del software que recibe dicho objeto es impredecible. Muchas implementaciones solo informan el apellido / par de valores. Otras implementaciones informan un error o no pueden analizar el objeto, y algunas implementaciones informan todos los pares de nombre / valor, incluidos los duplicados.



Además, como señaló Serguei en los comentarios: ECMA-262 "Especificación del lenguaje ECMAScript®", se lee:

En el caso de que haya cadenas de nombre duplicadas dentro de un objeto, se sobrescribirán los valores léxicamente anteriores para la misma clave.

En otras palabras, el último valor gana.


Intentar analizar una cadena con nombres duplicados con la implementación de Java por Douglas Crockford (el creador de JSON) da como resultado una excepción :

org.json.JSONException: Duplicate key "status"  at
org.json.JSONObject.putOnce(JSONObject.java:1076)
usuario454322
fuente
1
Se supone que JSON es JavaScript válido, por lo que es importante verificar si las claves duplicadas son JS válidas en literales. V8 parece aceptarlos: d8 -e 'x={"a":1,"a":2}; print(x.a);'esto imprime 2.
Ben Crowell
55
También la especificación ECMA-262 para JSON.parse()explícitamente diceIn the case where there are duplicate name Strings within an object, lexically preceding values for the same key shall be overwritten. (en otras palabras, last-value-wins).
Serguei
44
@BenCrowell: Que yo sepa, se supone que JSON no es JavaScript válido y hay casos en los que no lo es, consulte timelessrepo.com/json-isnt-a-javascript-subset . Dicho esto, por supuesto, estaba fuertemente inspirado por JavaScript (incluso lo dice en la especificación JSON).
Simon Touchtech
2
Vale la pena señalar que cuando se usa JavaScript "modo estricto", si hay dos claves idénticas, Chrome usará el segundo par clave-valor e ignorará el primero. IE11 lanzará una excepción.
Shahar
18

Hay 2 documentos que especifican el formato JSON:

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

La respuesta aceptada cita del primer documento. Creo que el primer documento es más claro, pero el segundo contiene más detalles.

El segundo documento dice:

  1. Objetos

    Una estructura de objeto se representa como un par de llaves entre cero o más pares de nombre / valor (o miembros). Un nombre es una cadena. Después de cada nombre, aparecen dos puntos, que separan el nombre del valor. Una coma simple separa un valor del siguiente nombre. Los nombres dentro de un objeto DEBEN ser únicos.

Por lo tanto, no está prohibido tener un nombre duplicado, pero se desaconseja.

toongeorges
fuente
10

Me encontré con una pregunta similar al tratar con una API que acepta XML y JSON, pero no documenta cómo manejaría lo que esperaría que fueran las claves duplicadas en el JSON aceptado.

La siguiente es una representación XML válida de su JSON de muestra:

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

Cuando esto se convierte en JSON, obtienes lo siguiente:

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

Un mapeo natural de un lenguaje que maneja lo que podría llamar claves duplicadas a otro, puede servir como una posible referencia de mejores prácticas aquí.

Espero que ayude a alguien!

un darren
fuente
5

La especificación JSON dice esto:

Un objeto es un conjunto desordenado de pares de nombre / valor.

La parte importante aquí es "desordenada": implica la unicidad de las claves, porque lo único que puede usar para referirse a un par específico es su clave.

Además, la mayoría de las bibliotecas JSON deserializarán los objetos JSON en mapas / diccionarios hash, donde se garantiza que las claves sean únicas. Lo que sucede cuando deserializa un objeto JSON con claves duplicadas depende de la biblioteca: en la mayoría de los casos, obtendrá un error o solo se tendrá en cuenta el último valor para cada clave duplicada.

Por ejemplo, en Python, json.loads('{"a": 1, "a": 2}')devuelve {"a": 2}.

Max Noel
fuente
23
¿Desordenado implica singularidad? Creo que establecer es la palabra
clave
8
Una colección desordenada de colores: azul, verde, verde, azul, rojo, azul, verde: tiene duplicados.
Timothy Shields
55
La frase de texto que está citando, "Un objeto es un conjunto desordenado de pares de nombre / valor", no aparece en la especificación JSON ...
Timothy Shields
66
Ahora veo que estaba citando a json.org. Está 'cerca' de oficial, pero no es la especificación. La parte superior de la página tiene un enlace a la especificación, que se repite textualmente en json.org. Si busca la especificación, la palabra "desordenada" no aparece, y la palabra "conjunto" aparece solo en contextos no relacionados con objetos JSON.
Timothy Shields
55
Tenga en cuenta que dice "conjunto desordenado de pares de nombre / valor ", pares no nombres. Es decir, { (a,b), (a,c) } es un conjunto único. Entonces, técnicamente bajo la definición de json.org {"a":1,"a":2}es válido pero {"a":1,"a":2,"a":1}no lo es. También tenga en cuenta que ECMA-404 (el estándar real) evita el uso de la palabra "set":An object structure is represented as a pair of curly bracket tokens surrounding zero or more name/value pairs.
Serguei
3

DEBE ser único no significa que DEBE ser único. Sin embargo, como se indicó, algunos analizadores fallarían y otros simplemente usarían el último valor analizado. Sin embargo, si la especificación se limpió un poco para permitir duplicados, entonces podría ver un uso en el que puede tener un controlador de eventos que está transformando el JSON a HTML u otro formato ... en tales casos, sería perfectamente válido para analizar el JSON y crear otro formato de documento ...

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

podría analizar fácilmente a html por ejemplo

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

Puedo ver el razonamiento detrás de la pregunta, pero tal como está ... No confiaría en eso.

Colin Saxton
fuente
Al conjunto de su primer ejemplo le falta una coma. Además, es inconsistente consigo mismo. Si va a usar un diccionario como una colección ordenada, su matriz externa también debería ser solo un objeto. Es decir, {"div":{"p":"hello","p":"universe"}, "div":{"h1":"Heading 1","p":"another paragraph"}}. Ahora, muchas personas y marcos tratan los objetos JSON como diccionarios desordenados, pero JavaScript y, por ejemplo, la API de MongoDB se basan en el orden de las claves dentro de los diccionarios, por lo que lo que estás sugiriendo (diccionarios ordenados) no es desconocido. Solo necesitarías un analizador especializado.
binki
2

Pidiendo propósito, hay diferentes respuestas:

Al usar JSON para serializar objetos (JavaScriptObjectNotation), cada elemento del diccionario se asigna a una propiedad de objeto individual, por lo que las diferentes entradas que definen un valor para la misma propiedad no tienen significado.

Sin embargo, surgió la misma pregunta de un caso de uso muy específico: al escribir muestras JSON para pruebas de API, me preguntaba cómo agregar comentarios a nuestro archivo JSON sin romper la usabilidad. La especificación JSON no conoce comentarios, así que se me ocurrió un enfoque muy simple:

Usar claves duplicadas para comentar nuestras muestras JSON . Ejemplo:

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

Los serializadores JSON que estamos utilizando no tienen problemas con estos duplicados "REMARK" y nuestro código de aplicación simplemente ignora esta pequeña sobrecarga.

Entonces, a pesar de que no hay significado en la capa de aplicación, estos duplicados nos brindan una solución valiosa para agregar comentarios a nuestras muestras de prueba sin romper la usabilidad del JSON.

aknoepfel
fuente
Esta es una mala idea. Al incluir claves duplicadas, incluso si no necesita leer los datos en ellas, confía en un comportamiento indefinido. Algunos analizadores, como el analizador JSON-java de Crockford, lanzan una excepción y se niegan a analizar los datos.
Richard Smith
En realidad, funciona perfectamente en nuestro entorno, por lo que satisfago mis necesidades, aunque estoy de acuerdo con usted en que está fuera de especificación;)
aknoepfel
@ RichardSmith Diría que los analizadores y las nuevas especificaciones de ES han definido el comportamiento.
binki
2

Publicar y responder porque hay muchas ideas obsoletas y confusión sobre los estándares. A diciembre de 2017, hay dos estándares en competencia:

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

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

json.org sugiere que ECMA-404 es el estándar, pero este sitio no parece ser una autoridad. Si bien creo que es justo considerar a ECMA como la autoridad, lo importante aquí es que la única diferencia entre los estándares (con respecto a las claves únicas) es que RFC 8259 dice que las claves deberían ser únicas, y el ECMA-404 dice que no se requiere que sean único.

RFC-8259:

"Los nombres dentro de un objeto DEBEN ser únicos".

La palabra "debería" en mayúsculas, tiene un significado dentro del mundo RFC, que se define específicamente en otro estándar (BCP 14, RFC 2119 - https://tools.ietf.org/html/rfc2119 ) como,

  1. Esta palabra, o el adjetivo "RECOMENDADO", significa que pueden existir razones válidas en circunstancias particulares para ignorar un elemento en particular, pero las implicaciones completas deben entenderse y sopesarse cuidadosamente antes de elegir un curso diferente.

ECMA-404:

"La sintaxis JSON no impone ninguna restricción en las cadenas utilizadas como nombres, no requiere que las cadenas de nombres sean únicas y no asigna ningún significado al orden de los pares de nombre / valor".

Entonces, no importa cómo lo corte, es JSON sintácticamente válido .

La razón dada para la recomendación clave única en RFC 8259 es,

Un objeto cuyos nombres son todos únicos es interoperable en el sentido de que todas las implementaciones de software que reciben ese objeto estarán de acuerdo en las asignaciones de nombre-valor. Cuando los nombres dentro de un objeto no son únicos, el comportamiento del software que recibe dicho objeto es impredecible. Muchas implementaciones solo informan el apellido / par de valores. Otras implementaciones informan un error o no pueden analizar el objeto, y algunas implementaciones informan todos los pares de nombre / valor, incluidos los duplicados.

En otras palabras, desde el punto de vista del RFC 8259, es válido, pero su analizador puede vomitar y no hay ninguna promesa sobre qué valor, si lo hay, se combinará con esa clave. Desde el punto de vista ECMA-404 (que personalmente tomaría como la autoridad), es válido, punto. Para mí, esto significa que cualquier analizador que se niegue a analizarlo está roto. Al menos debe analizar de acuerdo con estos dos estándares. Pero cómo se convierte en su objeto nativo de elección es, en cualquier caso, claves únicas o no, completamente dependientes del entorno y la situación, y nada de eso está en el estándar para empezar.

xthr33
fuente
json.org en realidad es anterior a la estandarización ECMA. Creo que en realidad fue creado por el propio Crockford (por eso tiene un enchufe descarado para su libro). En ese momento era la autoridad de JSON.
max
1

No está definido en el estándar ECMA JSON . Y en términos generales, la falta de definición en un estándar significa: "No cuente con que esto funcione de la misma manera en todas partes".

Si eres un jugador, "muchos" motores JSON permitirán la duplicación y simplemente usarán el último valor especificado. Esta:

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

Se convierte en esto:

Object {a: 3, b: 2}

Pero si no eres un jugador, ¡no cuentes con ello!

svidgen
fuente
1

El estándar dice esto:

Los lenguajes de programación varían ampliamente si admiten objetos y, de ser así, qué características y restricciones ofrecen los objetos. Los modelos de sistemas de objetos pueden ser muy divergentes y continúan evolucionando. En cambio, JSON proporciona una notación simple para expresar colecciones de pares de nombre / valor. La mayoría de los lenguajes de programación tendrán alguna característica para representar tales colecciones, que pueden ir por nombres como record, struct, dict, map, hash u object.

El error está en node.js al menos. Este código tiene éxito en node.js.

try {
     var json = {"name":"n","name":"v"};
     console.log(json); // outputs { name: 'v' }
} catch (e) {
     console.log(e);
}
John Carlson
fuente
1
No es un error, y la sección que citó explica por qué no lo es: los diferentes idiomas se comportan de manera diferente y los analizadores JSON harán lo que sea más natural para ese idioma. En cualquier caso, esta respuesta no agrega nada que el usuario 454322 no haya dicho anteriormente.
Richard Smith
1

Según RFC-7159, el estándar actual para JSON publicado por Internet Engineering Task Force (IETF), establece "Los nombres dentro de un objeto DEBEN ser únicos". Sin embargo, de acuerdo con RFC-2119, que define la terminología utilizada en los documentos de IETF, la palabra "debería" en realidad significa "... puede haber razones válidas en circunstancias particulares para ignorar un elemento en particular, pero las implicaciones completas deben ser entendidas y pesado cuidadosamente antes de elegir un curso diferente ". Lo que esto significa esencialmente es que, aunque se recomienda tener claves únicas, no es imprescindible. Podemos tener claves duplicadas en un objeto JSON, y aún sería válido.

Desde una aplicación práctica, he visto que el valor de la última clave se considera cuando se encuentran claves duplicadas en un JSON.

Navaneetha
fuente
0

En C # si deserializa a un Dictionary<string, string>toma el último par de valores clave:

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

si intentas deserializar

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

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

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

Tienes una Newtonsoft.Json.JsonSerializationExceptionexcepción.

Sam Leach
fuente
2
Supongo que eso es lo que hacen la mayoría (si no todas) las implementaciones, pero no responde a mi pregunta si es válido a partir de la especificación de JSON.
abrazadera
@svidgen: Esta ni siquiera es la implementación de Microsoft ... es una biblioteca de terceros.
BoltClock
@BoltClock Ah, touche.
svidgen