Creo que probablemente esté interesado en mostrar primero el campo ID (o similar) y luego todos los demás campos. esto es más amigable para los usuarios finales que buscarlo después de los campos que comienzan con A..I
Michael Bahig
3
Las propiedades JSON se definen como desordenadas. Creo que está absolutamente bien forzar un pedido de SALIDA en particular durante la serialización (tal vez por mirar el JSON tal vez), pero sería una mala decisión crear una DEPENDENCIA en cualquier orden particular de deserialización.
DaBlick
55
Un par de razones válidas: (1) falsificar una propiedad "$ type" que debe ser la primera propiedad en el JSON, (2) tratar de generar un JSON que se comprima tanto como sea posible
Stephen Chung
44
Otra razón podría ser (3) una representación canónica que utiliza la sintaxis JSON: se debe garantizar que el mismo objeto produzca la misma cadena JSON. Un orden determinista de los atributos es un requisito previo necesario para esto.
MarkusSchaber
2
Kevin, ¿puedes actualizar la respuesta aceptada a esta pregunta?
Millie Smith
Respuestas:
256
La forma admitida es utilizar el JsonPropertyatributo en las propiedades de clase para las que desea establecer el orden. Lea la documentación del pedido JsonPropertyAttribute para obtener más información.
Pasar el JsonPropertyun Ordervalor y el serializador se hará cargo del resto.
[JsonProperty(Order=1)]
Esto es muy similar al
DataMember(Order=1)
de los System.Runtime.Serializationdias.
Aquí hay una nota importante de @ kevin-babcock
... establecer el orden en 1 solo funcionará si establece un orden mayor que 1 en todas las demás propiedades. Por defecto, cualquier propiedad sin una configuración de Orden recibirá un orden de -1. Por lo tanto, debe proporcionar todas las propiedades y el orden serializados, o establecer su primer elemento en -2
El uso de la Orderpropiedad de JsonPropertyAttributese puede utilizar para controlar el orden en que los campos se serializan / deserializan. Sin embargo, establecer el orden en 1 solo funcionará si establece un orden mayor que 1 en todas las demás propiedades. Por defecto, cualquier propiedad sin una configuración de Orden recibirá un orden de -1. Por lo tanto, debe dar todas las propiedades y el orden serializados, o establecer su primer elemento en -2.
Kevin Babcock
1
Funciona para la serialización, pero el orden no se está considerando en la deserialización. Según la documentación, el atributo de pedido se utiliza tanto para la serialización como para la deserialización. ¿Hay alguna solución?
cangosta
1
¿Existe una propiedad similar para el JavaScriptSerializer.
Shimmy Weitzhandler
44
@cangosta El orden de deserialización no debería importar ... excepto en algunos casos de expectativas muy "extrañas".
usuario2864740
1
Lea la discusión de temas similares de Github sobre el deseo de que se respete el Orden en la deserialización: github.com/JamesNK/Newtonsoft.Json/issues/758 Básicamente no hay posibilidad de que esto ocurra .
Tyeth
126
En realidad se puede controlar el orden mediante la aplicación IContractResolvero anulando la DefaultContractResolver's CreatePropertiesmétodo.
Aquí hay un ejemplo de mi implementación simple IContractResolverque ordena las propiedades alfabéticamente:
Esto es bastante útil (+1) pero una advertencia: parece que la serialización de diccionarios no usa esta personalización CreateProperties. Serianizan bien pero no terminan ordenados. Supongo que hay una forma diferente de personalizar la serialización de diccionarios, pero no la he encontrado.
solublefish
Perfecto. Hace justo lo que quería. Gracias.
Wade Hatler
Esta es una gran solución. Funcionó perfectamente para mí, especialmente al poner 2 objetos JSON uno al lado del otro y hacer que las propiedades se alineen.
Vince
16
En mi caso, la respuesta de Mattias no funcionó. El CreatePropertiesmétodo nunca fue llamado.
Después de algunas depuraciones Newtonsoft.Jsoninternas, se me ocurrió otra solución.
publicclassJsonUtility{publicstaticstringNormalizeJsonString(string json){// Parse json string into JObject.var parsedObject =JObject.Parse(json);// Sort properties of JObject.var normalizedObject =SortPropertiesAlphabetically(parsedObject);// Serialize JObject .returnJsonConvert.SerializeObject(normalizedObject);}privatestaticJObjectSortPropertiesAlphabetically(JObject original){var result =newJObject();foreach(var property in original.Properties().ToList().OrderBy(p => p.Name)){varvalue= property.ValueasJObject;if(value!=null){value=SortPropertiesAlphabetically(value);
result.Add(property.Name,value);}else{
result.Add(property.Name, property.Value);}}return result;}}
Esta fue la solución necesaria para nosotros cuando usamos dicts.
noocyte
Esto agrega sobrecarga de deserialización y serialización adicionales. He agregado una solución que también funcionará para clases normales, diccionarios y ExpandoObject (objeto dinámico)
Jay Shah
11
En mi caso, la solución de niaher no funcionó porque no manejaba objetos en matrices.
Basado en su solución, esto es lo que se me ocurrió
Esto agrega sobrecarga de deserialización y serialización adicionales.
Jay Shah
Excelente solucion. Gracias.
Maya
3
Como señaló Charlie, puede controlar de alguna manera el orden de las propiedades JSON ordenando las propiedades en la clase misma. Desafortunadamente, este enfoque no funciona para propiedades heredadas de una clase base. Las propiedades de la clase base se ordenarán tal como se presentan en el código, pero aparecerán antes que las propiedades de la clase base.
Y para cualquiera que se pregunte por qué es posible que desee alfabetizar las propiedades JSON, es mucho más fácil trabajar con archivos JSON sin procesar, particularmente para clases con muchas propiedades, si están ordenadas.
¿No era este el comportamiento de pedido predeterminado durante la serialización?
Mr5
1
Para ahorrarle a alguien más unos minutos desperdiciados, tenga en cuenta que esta respuesta no funciona para los diccionarios a pesar del reclamo. CreatePropertiesno se invoca durante la serialización de un diccionario. Exploré el repositorio de JSON.net para saber qué maquinaria está pasando por las entradas del diccionario. No se engancha en ninguna overrideu otra personalización para ordenar. Solo toma las entradas como están del enumerador del objeto. Parece que tengo que construir SortedDictionaryo SortedListforzar a JSON.net a hacer esto. Sugerencia de función presentada: github.com/JamesNK/Newtonsoft.Json/issues/2270
William
2
Si no desea poner un JsonPropertyOrderatributo en cada propiedad de clase, entonces es muy simple hacer su propio ContractResolver ...
La interfaz IContractResolver proporciona una manera de personalizar cómo el JsonSerializer serializa y deserializa los objetos .NET a JSON sin colocar atributos en sus clases.
Me gusta esto:
privateclassSortedPropertiesContractResolver:DefaultContractResolver{// use a static instance for optimal performancestaticSortedPropertiesContractResolver instance;staticSortedPropertiesContractResolver(){ instance =newSortedPropertiesContractResolver();}publicstaticSortedPropertiesContractResolverInstance{get{return instance;}}protectedoverrideIList<JsonProperty>CreateProperties(Type type,MemberSerialization memberSerialization){var properties =base.CreateProperties(type, memberSerialization);if(properties !=null)return properties.OrderBy(p => p.UnderlyingName).ToList();return properties;}}
Implementar:
var settings =newJsonSerializerSettings{ContractResolver=SortedPropertiesContractResolver.Instance};var json =JsonConvert.SerializeObject(obj,Formatting.Indented, settings);
El siguiente método recursivo utiliza la reflexión para ordenar la lista de tokens internos en una JObjectinstancia existente en lugar de crear un nuevo gráfico de objetos ordenados. Este código se basa en los detalles internos de implementación de Json.NET y no debe usarse en producción.
voidSortProperties(JToken token){var obj = token asJObject;if(obj !=null){var props =typeof(JObject).GetField("_properties",BindingFlags.NonPublic|BindingFlags.Instance).GetValue(obj);var items =typeof(Collection<JToken>).GetField("items",BindingFlags.NonPublic|BindingFlags.Instance).GetValue(props);ArrayList.Adapter((IList) items).Sort(newComparisonComparer((x, y)=>{var xProp = x asJProperty;var yProp = y asJProperty;return xProp !=null&& yProp !=null?string.Compare(xProp.Name, yProp.Name):0;}));}foreach(var child in token.Children()){SortProperties(child);}}
Si controla (es decir, escribe) la clase, coloque las propiedades en orden alfabético y se serializarán en orden alfabético cuando JsonConvert.SerializeObject()se llame.
Quiero serializar un objeto comblex y mantener el orden de las propiedades tal como se definieron en el código. No puedo simplemente agregar [JsonProperty(Order = 1)]porque la clase en sí está fuera de mi alcance.
Esta solución también tiene en cuenta que las propiedades que se definen en una clase base deberían tener una prioridad más alta.
Esto puede no ser a prueba de balas, ya que no se define en ninguna parte que MetaDataAttributegarantice el orden correcto, pero parece funcionar. Para mi caso de uso, esto está bien. ya que solo quiero mantener la legibilidad humana para un archivo de configuración generado automáticamente.
publicclassPersonWithAge:Person{publicintAge{get;set;}}publicclassPerson{publicstringName{get;set;}}publicstringGetJson(){var thequeen =newPersonWithAge{Name="Elisabeth",Age=Int32.MaxValue};var settings =newJsonSerializerSettings(){ContractResolver=newMetadataTokenContractResolver(),};returnJsonConvert.SerializeObject(
thequeen,Newtonsoft.Json.Formatting.Indented, settings
);}publicclassMetadataTokenContractResolver:DefaultContractResolver{protectedoverrideIList<JsonProperty>CreateProperties(Type type,MemberSerialization memberSerialization){var props = type
.GetProperties(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic).ToDictionary(k => k.Name, v =>{// first value: declaring typevar classIndex =0;var t = type;while(t != v.DeclaringType){
classIndex++;
t = type.BaseType;}returnTuple.Create(classIndex, v.MetadataToken);});returnbase.CreateProperties(type, memberSerialization).OrderByDescending(p => props[p.PropertyName].Item1).ThenBy(p => props[p.PropertyName].Item1).ToList();}}
Acabo de ver los votos negativos. Consulte la respuesta de 'Steve' a continuación para saber cómo hacer esto.
ORIGINAL
Seguí la JsonConvert.SerializeObject(key)llamada al método a través de la reflexión (donde la clave era una IList) y descubrí que se llama a JsonSerializerInternalWriter.SerializeList. Toma una lista y recorre a través de
for (int i = 0; i < values.Count; i++) { ...
donde valores es el parámetro IList introducido.
La respuesta corta es ... No, no hay una forma integrada de establecer el orden en que los campos se enumeran en la cadena JSON.
@Darin, pero hay un orden en la serialización. "{id: 1, nombre: 'John'}" y "{nombre: 'John', id: 1}" son diferentes como cadenas , que es lo que me importa aquí. Por supuesto, los objetos son equivalentes cuando se deserializan.
Kevin Montrose
1
@Darin: no, no en este caso. Estoy serializando algo y luego pasándolo como una cadena a un servicio que solo se ocupa de cadenas (no es consciente de JSON), y sería conveniente por una variedad de razones para que un campo aparezca primero en la cadena.
Kevin Montrose
1
también es bueno para las pruebas, ya que solo puede mirar las cadenas en lugar de tener que deserializarlas.
Steve
9
Un orden de serialización estable también es útil para la validación de caché. Es trivial tomar una suma de verificación de una cadena, no es cierto para un gráfico de objeto completo.
solublefish
1
El orden de serialización también es útil cuando se realizan pruebas unitarias para que pueda decir fácilmente que las cadenas de respuesta esperadas frente a las reales son iguales incluso cuando el orden de las propiedades json es diferente.
Respuestas:
La forma admitida es utilizar el
JsonProperty
atributo en las propiedades de clase para las que desea establecer el orden. Lea la documentación del pedido JsonPropertyAttribute para obtener más información.Pasar el
JsonProperty
unOrder
valor y el serializador se hará cargo del resto.Esto es muy similar al
de los
System.Runtime.Serialization
dias.Aquí hay una nota importante de @ kevin-babcock
fuente
Order
propiedad deJsonPropertyAttribute
se puede utilizar para controlar el orden en que los campos se serializan / deserializan. Sin embargo, establecer el orden en 1 solo funcionará si establece un orden mayor que 1 en todas las demás propiedades. Por defecto, cualquier propiedad sin una configuración de Orden recibirá un orden de -1. Por lo tanto, debe dar todas las propiedades y el orden serializados, o establecer su primer elemento en -2.JavaScriptSerializer
.En realidad se puede controlar el orden mediante la aplicación
IContractResolver
o anulando laDefaultContractResolver
'sCreateProperties
método.Aquí hay un ejemplo de mi implementación simple
IContractResolver
que ordena las propiedades alfabéticamente:Y luego establezca la configuración y serialice el objeto, y los campos JSON estarán en orden alfabético:
fuente
En mi caso, la respuesta de Mattias no funcionó. El
CreateProperties
método nunca fue llamado.Después de algunas depuraciones
Newtonsoft.Json
internas, se me ocurrió otra solución.fuente
En mi caso, la solución de niaher no funcionó porque no manejaba objetos en matrices.
Basado en su solución, esto es lo que se me ocurrió
fuente
Como señaló Charlie, puede controlar de alguna manera el orden de las propiedades JSON ordenando las propiedades en la clase misma. Desafortunadamente, este enfoque no funciona para propiedades heredadas de una clase base. Las propiedades de la clase base se ordenarán tal como se presentan en el código, pero aparecerán antes que las propiedades de la clase base.
Y para cualquiera que se pregunte por qué es posible que desee alfabetizar las propiedades JSON, es mucho más fácil trabajar con archivos JSON sin procesar, particularmente para clases con muchas propiedades, si están ordenadas.
fuente
Esto funcionará también para clases normales, diccionarios y ExpandoObject (objeto dinámico).
fuente
CreateProperties
no se invoca durante la serialización de un diccionario. Exploré el repositorio de JSON.net para saber qué maquinaria está pasando por las entradas del diccionario. No se engancha en ningunaoverride
u otra personalización para ordenar. Solo toma las entradas como están del enumerador del objeto. Parece que tengo que construirSortedDictionary
oSortedList
forzar a JSON.net a hacer esto. Sugerencia de función presentada: github.com/JamesNK/Newtonsoft.Json/issues/2270Si no desea poner un
JsonProperty
Order
atributo en cada propiedad de clase, entonces es muy simple hacer su propio ContractResolver ...Me gusta esto:
Implementar:
fuente
El siguiente método recursivo utiliza la reflexión para ordenar la lista de tokens internos en una
JObject
instancia existente en lugar de crear un nuevo gráfico de objetos ordenados. Este código se basa en los detalles internos de implementación de Json.NET y no debe usarse en producción.fuente
En realidad, dado que mi Object ya era un JObject, utilicé la siguiente solución:
y luego úsalo así:
fuente
Si controla (es decir, escribe) la clase, coloque las propiedades en orden alfabético y se serializarán en orden alfabético cuando
JsonConvert.SerializeObject()
se llame.fuente
Quiero serializar un objeto comblex y mantener el orden de las propiedades tal como se definieron en el código. No puedo simplemente agregar
[JsonProperty(Order = 1)]
porque la clase en sí está fuera de mi alcance.Esta solución también tiene en cuenta que las propiedades que se definen en una clase base deberían tener una prioridad más alta.
Esto puede no ser a prueba de balas, ya que no se define en ninguna parte que
MetaDataAttribute
garantice el orden correcto, pero parece funcionar. Para mi caso de uso, esto está bien. ya que solo quiero mantener la legibilidad humana para un archivo de configuración generado automáticamente.fuente
Si desea configurar globalmente su API con campos ordenados, combine la respuesta de Mattias Nordberg:
con mi respuesta aquí:
¿Cómo forzar la API web ASP.NET para que siempre devuelva JSON?
fuente
ACTUALIZAR
Acabo de ver los votos negativos. Consulte la respuesta de 'Steve' a continuación para saber cómo hacer esto.
ORIGINAL
Seguí la
JsonConvert.SerializeObject(key)
llamada al método a través de la reflexión (donde la clave era una IList) y descubrí que se llama a JsonSerializerInternalWriter.SerializeList. Toma una lista y recorre a través defor (int i = 0; i < values.Count; i++) { ...
donde valores es el parámetro IList introducido.
La respuesta corta es ... No, no hay una forma integrada de establecer el orden en que los campos se enumeran en la cadena JSON.
fuente
No hay un orden de campos en el formato JSON, por lo que definir un orden no tiene sentido.
{ id: 1, name: 'John' }
es equivalente a{ name: 'John', id: 1 }
(ambos representan una instancia de objeto estrictamente equivalente)fuente