2010-07-25 29 views
78

¿Hay alguna manera de especificar el orden de los campos en un objeto JSON serializado usando JSON.NET?Orden de campos serializados usando JSON.NET

Sería suficiente especificar que un solo campo siempre aparece primero.

+0

Solo por curiosidad, ¿por qué te importa? –

+3

creo que probablemente esté interesado en mostrar el campo ID (o similar) primero, y luego todos los demás campos. esto es más amistoso para los usuarios finales que buscarlo después de los campos que comienzan con A..I – michaelAngelo

+1

Las propiedades JSON se definen como desordenadas. Creo que es absolutamente correcto forzar un pedido de SALIDA en particular durante la serialización (por el simple hecho de echar un vistazo al JSON), pero sería una mala decisión crear una DEPENDENCIA en cualquier orden particular en deserialización. – DaBlick

Respuesta

-1

que siguieron la llamada JsonConvert.SerializeObject(key) método a través de la reflexión (donde la clave era un IList) y encontraron que JsonSerializerInternalWriter.SerializeList se llama. Toma una lista y recorre a través de

for (int i = 0; i < values.Count; i++) { ...

donde los valores es el parámetro IList trajeron.

respuesta corta es ... No, no hay construido en forma de establecer el orden de los campos son enumerados en la cadena JSON.

+9

Respuesta breve, pero posiblemente desactualizada. Echa un vistazo a la respuesta de Steve (con el apoyo de James Newton-king) –

-3

No hay orden de campos en el formato JSON, por lo que definir un pedido no tiene sentido.

{ id: 1, name: 'John' } es equivalente a { name: 'John', id: 1 } (ambos representan una instancia de objeto estrictamente equivalente)

+0

Esto solo importa para la transmisión "en el cable", no para su consumo en el otro lado. Una vez deserializado, no espero que la orden persista, básicamente. –

+0

¿De qué orden estás hablando? JSON es un formato que permite la serialización/deserialización de objetos. No hay ningún orden * de idea * en los campos de objeto en OOP. –

+9

@Darin - pero hay un pedido en la serialización. "{id: 1, name: 'John'}" y "{name: 'John', id: 1}" son diferentes como * cadenas *, que es lo que me importa aquí. Por supuesto, los objetos son equivalentes cuando se deserializan. –

88

En realidad, puede controlar el orden implementando IContractResolver o anulando el DefaultContractResolver del método CreateProperties.

He aquí un ejemplo de mi sencilla implementación de IContractResolver que ordena las propiedades alfabéticamente:

public class OrderedContractResolver : DefaultContractResolver 
{ 
    protected override System.Collections.Generic.IList<JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization) 
    { 
     return base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName).ToList(); 
    } 
} 

y después fijar los parámetros y serializar el objeto y los campos JSON será en orden alfabético:

var settings = new JsonSerializerSettings() 
{ 
    ContractResolver = new OrderedContractResolver() 
}; 

var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings); 
+1

funcionó perfectamente para mí – michaelAngelo

+0

gracias! ¡Estaba buscando eso! – esskar

+3

Esto es bastante útil (+1) pero una advertencia: parece que la serialización de diccionarios no usa esta personalización CreateProperties. Se serializan bien pero no terminan ordenados. Supongo que hay una forma diferente de personalizar la serialización de diccionarios, pero no la he encontrado. – solublefish

0

El siguiente método recursivo utiliza la reflexión para ordenar la lista de token interno en una instancia de JObject existente en lugar de crear un br y nuevo gráfico de objetos ordenados. Este código se basa en los detalles de la implementación interna de Json.NET y no debe utilizarse en producción.

void SortProperties(JToken token) 
{ 
    var obj = token as JObject; 
    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(new ComparisonComparer(
       (x, y) => 
       { 
        var xProp = x as JProperty; 
        var yProp = y as JProperty; 
        return xProp != null && yProp != null 
         ? string.Compare(xProp.Name, yProp.Name) 
         : 0; 
       })); 
    } 
    foreach (var child in token.Children()) 
    { 
     SortProperties(child); 
    } 
} 
168

La forma soportada es utilizar el atributo JsonProperty sobre las propiedades de la clase que desea establecer el orden de. Read the JsonPropertyAttribute order documentation for more information.

Pase el JsonProperty un valor Order y el serializador se encargará del resto.

[JsonProperty(Order = 1)] 

Esto es muy similar a la

DataMember(Order = 1) 

de los System.Runtime.Serialization días.

+61

El uso de la propiedad 'Order' de' JsonPropertyAttribute' se puede usar 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. De manera predeterminada, cualquier propiedad sin una configuración de Orden recibirá un orden de -1. Por lo tanto, debe dar todas las propiedades serializadas y el orden, o establecer su primer elemento en -2. –

+0

Esta solución funcionó perfectamente para mí.^5 –

+1

Funciona para la serialización, pero el orden no se está considerando en la deserialización. De acuerdo con la documentación, el atributo de orden se usa tanto para la serialización como para la deserialización. ¿Hay alguna solución? – cangosta

10

En mi caso, la respuesta de Mattias no funcionó. El método CreateProperties nunca fue llamado.

Después de algunas depuraciones de Newtonsoft.Json partes internas, se me ocurrió otra solución.

public class JsonUtility 
{ 
    public static string NormalizeJsonString(string json) 
    { 
     // Parse json string into JObject. 
     var parsedObject = JObject.Parse(json); 

     // Sort properties of JObject. 
     var normalizedObject = SortPropertiesAlphabetically(parsedObject); 

     // Serialize JObject . 
     return JsonConvert.SerializeObject(normalizedObject); 
    } 

    private static JObject SortPropertiesAlphabetically(JObject original) 
    { 
     var result = new JObject(); 

     foreach (var property in original.Properties().ToList().OrderBy(p => p.Name)) 
     { 
      var value = property.Value as JObject; 

      if (value != null) 
      { 
       value = SortPropertiesAlphabetically(value); 
       result.Add(property.Name, value); 
      } 
      else 
      { 
       result.Add(property.Name, property.Value); 
      } 
     } 

     return result; 
    } 
} 
+0

Esta fue la solución necesaria para nosotros al usar dicts. – noocyte

0

En realidad, ya que mi objeto era ya un jobject, he utilizado la siguiente solución:

public class SortedJObject : JObject 
{ 
    public SortedJObject(JObject other) 
    { 
     var pairs = new List<KeyValuePair<string, JToken>>(); 
     foreach (var pair in other) 
     { 
      pairs.Add(pair); 
     } 
     pairs.OrderBy(p => p.Key).ForEach(pair => this[pair.Key] = pair.Value); 
    } 
} 

y luego usarlo como esto:

string serializedObj = JsonConvert.SerializeObject(new SortedJObject(dataObject)); 
7

En solución mi caso de niaher hizo no funciona porque no manejaba objetos en matrices.

Sobre la base de su solución Esto es lo que ocurrió con

public static class JsonUtility 
{ 
    public static string NormalizeJsonString(string json) 
    { 
     JToken parsed = JToken.Parse(json); 

     JToken normalized = NormalizeToken(parsed); 

     return JsonConvert.SerializeObject(normalized); 
    } 

    private static JToken NormalizeToken(JToken token) 
    { 
     JObject o; 
     JArray array; 
     if ((o = token as JObject) != null) 
     { 
      List<JProperty> orderedProperties = new List<JProperty>(o.Properties()); 
      orderedProperties.Sort(delegate(JProperty x, JProperty y) { return x.Name.CompareTo(y.Name); }); 
      JObject normalized = new JObject(); 
      foreach (JProperty property in orderedProperties) 
      { 
       normalized.Add(property.Name, NormalizeToken(property.Value)); 
      } 
      return normalized; 
     } 
     else if ((array = token as JArray) != null) 
     { 
      for (int i = 0; i < array.Count; i++) 
      { 
       array[i] = NormalizeToken(array[i]); 
      } 
      return array; 
     } 
     else 
     { 
      return token; 
     } 
    } 
} 
0

Si el control (es decir, escribir) la clase, poner las propiedades en orden alfabético y van a serializar en orden alfabético cuando JsonConvert.SerializeObject() se llama.

2

Como observó Charlie, puede controlar el orden de las propiedades JSON ordenando las propiedades de la clase. Desafortunadamente, este enfoque no funciona para las 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 de las propiedades de la clase base.

Y para cualquier persona que se pregunte por qué es posible que desee alfabetizar propiedades JSON, es mucho más fácil trabajar con archivos JSON sin procesar, especialmente para clases con muchas propiedades, si se solicitan.

Cuestiones relacionadas