2011-03-07 5 views
34

J'ai un peu de mal à désérialiser les données renvoyées par Facebook en utilisant les librairies JSON.NET.Deserializing JSON when parfois array et parfois object

Le JSON retourné juste d'un simple poste de mur ressemble:

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

Le JSON retourné une photo ressemble:

"attachment":{ 
     "media":[ 
      { 
       "href":"http://www.facebook.com/photo.php?fbid=12345", 
       "alt":"", 
       "type":"photo", 
       "src":"http://photos-b.ak.fbcdn.net/hphotos-ak-ash1/12345_s.jpg", 
       "photo":{"aid":"1234","pid":"1234","fbid":"1234","owner":"1234","index":"12","width":"720","height":"482"}} 
     ], 

Tout fonctionne très bien et je n'ai pas de problème. Je suis maintenant venu à travers un poste simple mur d'un client mobile avec le JSON suivant, et désérialisation échoue maintenant avec celui-ci seul poste:

"attachment": 
    { 
     "media":{}, 
     "name":"", 
     "caption":"", 
     "description":"", 
     "properties":{}, 
     "icon":"http://www.facebook.com/images/icons/mobile_app.gif", 
     "fb_object_type":"" 
    }, 
"permalink":"http://www.facebook.com/1234" 

Voici la classe que je suis désérialisation comme:

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<FacebookMedia> { get; set; } 

     public string Permalink { get; set; } 
    } 

Sans utiliser FacebookMediaJsonConverter, j'obtiens une erreur: Impossible de désérialiser l'objet JSON dans le type 'System.Collections.Generic.List`1 [FacebookMedia]'. ce qui est logique, puisque dans le JSON, Media n'est pas une collection.

Je trouve ce poste qui décrit un problème similaire, donc j'ai essayé d'aller dans cette voie: Deserialize JSON, sometimes value is an array, sometimes "" (blank string)

Mon convertisseur ressemble:

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

Ce qui fonctionne très bien, sauf que je maintenant obtenir une nouvelle exception:

intérieur JsonSerializerInternalReader.cs, CreateValueInternal(): jeton inattendue lors de la désérialisation objet: NomPropriété

La valeur de reader.Value est "permalink". Je peux clairement voir dans le commutateur qu'il n'y a aucun cas pour JsonToken.PropertyName.

Y at-il quelque chose que je dois faire différemment dans mon convertisseur? Merci pour toute aide.

Répondre

20

le développeur de JSON.NET a fini par aider sur les projets de site CodePlex Voici la solution:..

le problème a été, quand il était un objet JSON, je ne lisait pas passé l'attribut. Voici le code correct:

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

James a également eu la gentillesse de fournir des tests unitaires pour la méthode ci-dessus.

2

jetez un coup d'œil à l'espace de noms System.Runtime.Serialization dans le framework C#, cela vous mènera là où vous voulez être très rapidement.

Si vous voulez, vous pouvez consulter quelques exemples de code dans le projet this (ne pas essayer de brancher mon propre travail, mais je viens de terminer à peu près exactement ce que vous faites, mais avec une api source différente.

espère qu'il aide

+1

-1 pour une nouvelle utilisation d'une partie interne prouvée du framework sur une bibliothèque tierce avec des liens vers des exemples de code? – jonezy

+0

cela semble très inhabituel. – jonezy

+0

Je ne suis pas sûr d'où vient le -1. J'apprécie la réponse. Je soupçonne que c'est probablement une solution assez lourde à un problème mineur (probablement dû à ma mauvaise utilisation de JSON.NET). Cela étant dit, je vais essayer les méthodes Systme.Runtime.Serialization et voir si ça finit par mieux fonctionner. Merci pour le lien. – mfanto

-2

Je pense que vous devriez écrire votre classe comme ça ... !!!

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 
    { 
    } 
19

Une explication très détaillée sur la façon de gérer ce cas est disponible au "Using a Custom JsonConverter to fix bad JSON results".

Pour résumer, vous pouvez étendre le convertisseur de JSON.NET par défaut faisant

  1. annote la propriété avec la question

    [JsonConverter(typeof(SingleValueArrayConverter<OrderItem>))] 
    public List<OrderItem> items; 
    
  2. Étendre le convertisseur pour retourner une liste de votre type désiré même pour un seul objet

    public class SingleValueArrayConverter<T> : 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<T>() { instance }; 
         } else if (reader.TokenType == JsonToken.StartArray) { 
          retVal = serializer.Deserialize(reader, objectType); 
         } 
         return retVal; 
        } 
    
        public override bool CanConvert(Type objectType) 
        { 
         return true; 
        } 
    } 
    

Comme mentionné dans l'article, cette extension n'est pas complètement générale, mais cela fonctionne si vous avez une liste.

+0

Absolument parfait. Je vous remercie. – NotMe

+0

Je suis confus - mon WriteJson ne lance pas implémenté? Ai-je besoin de gérer cela? – nologo

Questions connexes