Deserialización de JSON cuando a veces la matriz y a veces el objeto

Estoy teniendo problemas para deserializar los datos devueltos por Facebook utilizando las bibliotecas JSON.NET.

El JSON devuelto de una simple publicación en el muro se ve así:

{ "attachment":{"description":""}, "permalink":"http://www.facebook.com/permalink.php?story_fbid=123456789" } 

El JSON devuelto para una foto se ve así:

 "attachment":{ "media":[ { "href":"http://www.facebook.com/photo.php?fbid=12345", "alt":"", "type":"photo", "src":"http://sofes.miximages.com/c%23/12345_s.jpg", "photo":{"aid":"1234","pid":"1234","fbid":"1234","owner":"1234","index":"12","width":"720","height":"482"}} ], 

Todo funciona bien y no tengo problemas. Ahora me he encontrado con una simple publicación en la pared de un cliente móvil con el siguiente JSON, y la deserialización ahora falla con esta única publicación:

 "attachment": { "media":{}, "name":"", "caption":"", "description":"", "properties":{}, "icon":"http://sofes.miximages.com/c%23/mobile_app.gif", "fb_object_type":"" }, "permalink":"http://www.facebook.com/1234" 

Aquí está la clase que estoy deserializando como:

 public class FacebookAttachment { public string Name { get; set; } public string Description { get; set; } public string Href { get; set; } public FacebookPostType Fb_Object_Type { get; set; } public string Fb_Object_Id { get; set; } [JsonConverter(typeof(FacebookMediaJsonConverter))] public List { get; set; } public string Permalink { get; set; } } 

Sin utilizar FacebookMediaJsonConverter, aparece un error: No se puede deserializar el objeto JSON en el tipo ‘System.Collections.Generic.List`1 [FacebookMedia]’. lo cual tiene sentido, ya que en el JSON, Media no es una colección.

Encontré esta publicación que describe un problema similar, así que he intentado seguir esta ruta: Deserializar JSON, a veces el valor es una matriz, a veces “” (cadena en blanco)

Mi convertidor se ve así:

 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.StartArray) return serializer.Deserialize<List>(reader); else return null; } 

Lo cual funciona bien, excepto que ahora recibo una nueva excepción:

Dentro de JsonSerializerInternalReader.cs, CreateValueInternal (): Token inesperado al deserializar el objeto: PropertyName

El valor de reader.Value es “permanente”. Puedo ver claramente en el interruptor que no hay ningún caso para JsonToken.PropertyName.

¿Hay algo que deba hacer de manera diferente en mi convertidor? Gracias por cualquier ayuda.

Encontrará una explicación muy detallada sobre cómo manejar este caso en “Uso de un JsonConverter personalizado para corregir los malos resultados de JSON” .

Para resumir, puede extender el convertidor JSON.NET predeterminado haciendo

  1. Anotar la propiedad con el problema

     [JsonConverter(typeof(SingleValueArrayConverter))] public List items; 
  2. Extienda el convertidor para devolver una lista de su tipo deseado incluso para un solo objeto

     public class SingleValueArrayConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { object retVal = new Object(); if (reader.TokenType == JsonToken.StartObject) { T instance = (T)serializer.Deserialize(reader, typeof(T)); retVal = new List() { instance }; } else if (reader.TokenType == JsonToken.StartArray) { retVal = serializer.Deserialize(reader, objectType); } return retVal; } public override bool CanConvert(Type objectType) { return true; } } 

Como se menciona en el artículo, esta extensión no es completamente general, pero funciona si no tiene inconveniente en obtener una lista.

El desarrollador de JSON.NET terminó ayudando en el sitio Codeplex de los proyectos. Aquí está la solución:

El problema era que, cuando era un objeto JSON, no estaba leyendo más allá del atributo. Aquí está el código correcto:

 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.StartArray) { return serializer.Deserialize>(reader); } else { FacebookMedia media = serializer.Deserialize(reader); return new List(new[] {media}); } } 

James también tuvo la amabilidad de proporcionar pruebas unitarias para el método anterior.

Eche un vistazo al espacio de nombres System.Runtime.Serialization en el framework c #, lo llevará a donde desea estar muy rápidamente.

Si lo desea, puede consultar algún código de ejemplo en este proyecto (sin intentar conectar mi propio trabajo, pero acabo de terminar casi exactamente lo que está haciendo, pero con una API diferente.

Espero eso ayude.

¡¡Creo que deberías escribir tu clase así … !!!

 public class FacebookAttachment { [JsonProperty("attachment")] public Attachment Attachment { get; set; } [JsonProperty("permalink")] public string Permalink { get; set; } } public class Attachment { [JsonProperty("media")] public Media Media { get; set; } [JsonProperty("name")] public string Name { get; set; } [JsonProperty("caption")] public string Caption { get; set; } [JsonProperty("description")] public string Description { get; set; } [JsonProperty("properties")] public Properties Properties { get; set; } [JsonProperty("icon")] public string Icon { get; set; } [JsonProperty("fb_object_type")] public string FbObjectType { get; set; } } public class Media { } public class Properties { }