3

I am trying to make an ajax call to get the states from a country but i keep getting an invalidcastexception. I am using MVC4 with json.NET. I can serialize the objects without a problem in my tests, but when i make the ajax call I keep getting an error.

This is the value type:

[JsonConverter(typeof(ValueObjectConverter))]
public class Code : IValueObject
{
    private readonly string _code;

    private Code(string code)
    {
        _code = code;
    }

    public override string ToString()
    {
        return _code;
    }

    public static implicit operator Code(string code)
    {
        return new Code(code);
    }

    public static implicit operator String(Code code)
    {
        return code.ToString();
    }
}

This is my object:

public class CountryState : Entity
{
    public Code CodeWPS { get; set; }
    public Code CodeIAM { get; set; }
    public Name Description { get; set; }
}

This is the jsonconverter:

public class ValueObjectConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(IValueObject));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return (IValueObject)existingValue;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var item = (IValueObject)value;
        writer.WriteValue(item.ToString());
        writer.Flush();
    }
}

This is the API method:

    public IEnumerable<CountryState> GetStatesByCountry(string codeType, string countryCodeIam)
    {
        var states = _getAllCountriesQueryHandler.Handle(countryCodeIam);
        return states;
    }

This is the error i get with firebug:

    {"Message":"An error has occurred.","ExceptionMessage":"The 'ObjectContent`1' type    failed to serialize the response body for content type 'application/json; charset=utf-8'.","ExceptionType":"System.InvalidOperationException","StackTrace":null,"InnerException":{"Message":"An error has occurred.","ExceptionMessage":"Error getting value from 'CodeWPS' on 'CNH.CSCN.BBS.Entities.CountryState'.","ExceptionType":"Newtonsoft.Json.JsonSerializationException","StackTrace":"   at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)\r\n   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue)\r\n   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)\r\n   at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)\r\n   at System.Net.Http.Formatting.JsonMediaTypeFormatter.<>c__DisplayClassd.<WriteToStreamAsync>b__c()\r\n   at System.Threading.Tasks.TaskHelpers.RunSynchronously(Action action, CancellationToken token)","InnerException":{"Message":"An error has occurred.","ExceptionMessage":"Unable to cast object of type 'System.String' to type 'CNH.CSCN.BBS.Entities.ValueTypes.Code'.","ExceptionType":"System.InvalidCastException","StackTrace":"   at GetCodeWPS(Object )\r\n   at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)"}}}

UPDATE:

I have added a contractresolver:

public class SpecialContractResolver : DefaultContractResolver
{
    protected override IValueProvider CreateMemberValueProvider(MemberInfo member)
    {
        if (member.MemberType == MemberTypes.Property)
        {
            var pi = (PropertyInfo)member;
            if (typeof(IValueObject).IsAssignableFrom(pi.PropertyType))
            {
                return new IValueObjectValueProvider(member, pi.PropertyType);
            }
        }
        else if (member.MemberType == MemberTypes.Field)
        {
            var fi = (FieldInfo)member;
            if (typeof(IValueObject).IsAssignableFrom(fi.FieldType))
            {
                return new IValueObjectValueProvider(member, fi.FieldType);
            }
        }

        return base.CreateMemberValueProvider(member);
    }
}

and a IValueProvider:

public class IValueObjectValueProvider : IValueProvider
{
    private readonly object _defaultValue;
    private readonly IValueProvider _underlyingValueProvider;


    public IValueObjectValueProvider(MemberInfo memberInfo, Type underlyingType)
    {
        _underlyingValueProvider = new DynamicValueProvider(memberInfo);
        _defaultValue = underlyingType;
    }

    public void SetValue(object target, object value)
    {
        target = value;
    }

    public object GetValue(object target)
    {
        return target.ToString();
    }
}

I know I am on the right track with this, I no longer get the error message, but this result:

Object { CodeWPS="CNH.CSCN.BBS.Entities.CountryState", CodeIAM="CNH.CSCN.BBS.Entities.CountryState", Description="CNH.CSCN.BBS.Entities.CountryState"}

I think my contractresolver and ivalueprovider are not a 100% correct yet...

1
  • After further investigation it looks like he is not using my custom converter when making the ajax call. Commented Aug 9, 2013 at 12:01

2 Answers 2

1

It's because of this piece of code:

public override bool CanConvert(Type objectType)
{
    return (objectType == typeof(IValueObject));
}

Your objectType isn't IValueObject, it's Code - so CanConvert returns false and your converter is not used. Your code should look like this:

return typeof(IValueObject).IsAssignableFrom(objectType);

With this condition, implementing classes are matched, too.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for the tip, unfortunately this does not resolve my invalid cast exception.The CanConvert gets these inputs: type list - type countrystate - type httperror - type string...
You get the casting error because you try to unbox the existingValue to IValueObject which is not possible because it isn't deserialized yet. You have to deserialize the JSON to a C# string first and then convert it to a Code object which is pretty complicated because you only have the implicit type cast operator to create a new Code object. If your constructor was public, you could write something like return Activator.CreateInstance(objectType, serializer.Deserialize<string>(reader)); or you can use another overload of CreateInstance to use a private constructor.
He never reaches my ReadJson or WriteJson methods, I'm looking into the DynamicValueProvider of JSON.NET right now to see how it works...
0

I have created a SpecialContractResolver

public class SpecialContractResolver : DefaultContractResolver
{
    protected override IValueProvider CreateMemberValueProvider(MemberInfo member)
    {
        if (member.MemberType == MemberTypes.Property)
        {
            var pi = (PropertyInfo)member;
            if (typeof(IValueObject).IsAssignableFrom(pi.PropertyType))
            {
                return new ExpressionValueProvider(member);
            }
        }

        return base.CreateMemberValueProvider(member);
    }
}

I created a ValueObjectConverter:

public class ValueObjectConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(IValueObject).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return Activator.CreateInstance(objectType, serializer.Deserialize<string>(reader));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var item = (IValueObject)value;
        writer.WriteValue(item.ToString());
        writer.Flush();
    }
}

And i added it to my WebApiConfig:

        var jsonFormatter = config.Formatters.JsonFormatter;
        jsonFormatter.SerializerSettings.ContractResolver = new SpecialContractResolver();
        jsonFormatter.SerializerSettings.Converters.Insert(0, new ValueObjectConverter());

Hope this helps someone!

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.