xamarin / azure-mobile-services

Apache License 2.0
48 stars 25 forks source link

JsonArrays do not deserialize (throw an exception instead) #2

Closed tkaczmarczyk closed 11 years ago

tkaczmarczyk commented 11 years ago

On MonoTouch (and I'd expect on other platforms as well) the library can't deserialize JsonArrays (even if they are ignored) and throws an exception instead.

Steps to reproduce

  1. in a read script for an arbitrary AMS table paste something along the lines of:
function read(query, user, request) {
  request.execute({
    success : function(results) {
      for(var i=0; i<results.length; i++) results[i].ThisIsAnArrayProperty = [1,2,3];
      request.respond(statusCodes.OK, results);
    }
  });
}
  1. Attempt to read from the table using the provided AMS libs

What happens app crashes with System.InvalidCastException: Unable to cast object of type 'Windows.Data.Json.JsonArray' to type 'Windows.Data.Json.JsonValue'. on azure-mobile-services-master/sdk/xamarin/android/src/Microsoft.Azure.Zumo.Android/Windows.Data.Json/JsonObject.cs:56 (public JsonValue GetNamedValue (string name))

What should happen the array property should be deserialized into a JsonArray.

Possible fix (worked for me) Change return type of the GetNamedValue method to a more general IJsonValue and propagate the type change across all usages - worked for me and didn't seem to break anything.

From:

    public JsonValue GetNamedValue (string name)
    {
        return (JsonValue)this[name];
    }

To:

    public IJsonValue GetNamedValue (string name)
    {
        return (IJsonValue)this[name];
    }
ignaciofuentes commented 11 years ago

I second your fix. Modified it and we´re not getting that error anymore.

ermau commented 11 years ago

Can you show the client side code to reproduce this?

ignaciofuentes commented 11 years ago

So the scenario where I encountered this error was something like: Say I have a dinner object and Ingredients object. on the server, I modify the read script so that the dinner is populated with an ingredients array. on the client I add the property to the Dinner model class:

[DataMemberJsonConverter(ConverterType=typeof(IngredientListConverter))]    
public List<Ingredient> Ingredients { get; set;}

The problem is that when I try to perform a simple read:

dinnersTable.Take(pageSize).Skip(pageSize*page).ToListAsync()
            .ContinueWith (t =>                
               {
                 var dinners = from item in t.Result                    
                select new StringElement(dinner.Name) as Element;
                  //do more stuff                     
             }, scheduler)

the code doesn't ever reach the converter at all. It throws the InvalidCastException before it can get to.

By applying the proposed fix, it does. And the ingredients property gets populated properly.

Just in case you are wondering. My IngredientListConverter is:

    public class IngredientListConverter : 
    IDataMemberJsonConverter
{
    public object ConvertFromJson(IJsonValue value)
    {
        return value.GetArray().Select(c =>
                                       MobileServiceTableSerializer.
                                       Deserialize<Ingredient>(c)).ToList();
    }
    public IJsonValue ConvertToJson(object instance)
    {
        return null;
    }
}
ermau commented 11 years ago

Since we're going to be transitioning over to the PCL based library which instead uses JSON.NET for its JSON, I'm going to go ahead and close this as it will be a non-issue in the future.