Deserializing when sometimes array, sometimes object

Mar 7, 2011 at 4:48 AM

 

I'm having a bit of trouble deserializing data returned from Facebook.

The JSON returned from just a simple wall post looks like:

 

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


The JSON returned for a photo looks like:
   "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"}}
        ],


Everything works great and I have no problems. I've now come across a simple wall post from a mobile client with the following JSON, and deserialization now fails with this one single post:

"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"

Here is the class I am deserializing as:

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; }
    }


Without using the FacebookMediaJsonConverter, I get an error: Cannot deserialize JSON object into type 'System.Collections.Generic.List`1[FacebookMedia]'.
which makes sense, since in the JSON, Media is not a collection.

My converter looks like:
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;
}

Which works fine, except I now get a new exception:

Inside JsonSerializerInternalReader.cs, CreateValueInternal(): Unexpected token while deserializing object: PropertyName

The value of reader.Value is "permalink". I can clearly see in the switch that there's no case for JsonToken.PropertyName.

Is there something I need to do differently in my converter? Thanks for any help.

Best,
Matt
Mar 16, 2011 at 10:59 AM

Hi there, I've had a similar issue too. I'm consuming an API I have no control over. JSON is inconsistent when sending single or multiple. In the end, I tweaked a couple of objects.

To be honest, I only picked up the library this morning so may have chose COMPLETELY the wrong path to go down. Any feedback on my methods would be greatly appreciated. Be aware, this will only fix the problem if you have an array property but I imagine you could extend it easily enough to take in Lists etc. The changes I made are as follows:

In JsonArrayContract

- Add a new member variable

private JsonObjectContract

_elementContract;

 

 
 

 

 

    

 

- Alter the constructor to take an extra attribute

public JsonArrayContract(Type underlyingType, IContractResolver resolver)

 

 - Add a new line to the top of the constructor

 _elementContract = resolver.ResolveContract(underlyingType.GetElementType()) as JsonObjectContract;

- Add a public property to wrap the contract object

 

 

 

 

public JsonObjectContract ElementContract { get { return

_elementContract; } }

 

 

 

 In DefaultContractResolver

 In line 400 (on my version anyway), change the constructor of the array contract to also take "this"

JsonArrayContract contract = new JsonArrayContract(objectType, this);

In JsonSerializerInternalReader

Add the following block of code to CreateObject, just after an existing block of code that handles the object contract. (Line 408 in my version)

JsonArrayContract arrayContract = contract as JsonArrayContract;

if (arrayContract != null)

{

 

 

 

 

 

 

 

 

 

As I pointed out, this is hastily done but solves my issue.

 

object arrayMember = CreateAndPopulateObject(reader, arrayContract.ElementContract, id);

Array a = Array .CreateInstance(arrayContract.ElementContract.UnderlyingType, 1);

a.SetValue(arrayMember, 0);

return a;

 

}