Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeConverter is ignored when reading a value that implements IConvertible #676

Closed
ashmind opened this issue Oct 5, 2015 · 8 comments
Closed

Comments

@ashmind
Copy link

ashmind commented Oct 5, 2015

Example code (https://dotnetfiddle.net/iGB2mc):

using System;
using System.ComponentModel;
using System.Globalization;
using Newtonsoft.Json;

public class Program {
    public static void Main() {
        var simple = JsonConvert.DeserializeObject<X>("\"Simple\"");    
        Console.WriteLine(simple.Value);
    }
}

[TypeConverter(typeof(XTypeConverter))]
public sealed class X : IConvertible {
    public X(string value) {
        Value = value;
    }

    public string Value { get; private set; }

    TypeCode IConvertible.GetTypeCode() { return TypeCode.Object; } 
    bool IConvertible.ToBoolean(IFormatProvider provider)  { return false; }    
    char IConvertible.ToChar(IFormatProvider provider)     { return '\0'; }
    sbyte IConvertible.ToSByte(IFormatProvider provider)   { return 0; }    
    byte IConvertible.ToByte(IFormatProvider provider)     { return 0; }    
    short IConvertible.ToInt16(IFormatProvider provider)   { return 0; }    
    ushort IConvertible.ToUInt16(IFormatProvider provider) { return 0; }
    int IConvertible.ToInt32(IFormatProvider provider)     { return 0; }    
    uint IConvertible.ToUInt32(IFormatProvider provider)   { return 0; }    
    long IConvertible.ToInt64(IFormatProvider provider)    { return 0; }    
    ulong IConvertible.ToUInt64(IFormatProvider provider)  { return 0; }    
    float IConvertible.ToSingle(IFormatProvider provider)  { return 0; }    
    double IConvertible.ToDouble(IFormatProvider provider) { return 0; }    
    decimal IConvertible.ToDecimal(IFormatProvider provider) { return 0;}   
    DateTime IConvertible.ToDateTime(IFormatProvider provider) { return DateTime.MinValue; }    
    string IConvertible.ToString(IFormatProvider provider) { return ""; }   
    object IConvertible.ToType(Type conversionType, IFormatProvider provider) { return null; }
}

public class XTypeConverter : TypeConverter {
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
        return sourceType == typeof(string);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
        var @string = value as string;
        if (@string != null)
            return new X(@string);

        throw new NotSupportedException();
    }
}

Produces

Newtonsoft.Json.JsonSerializationException: Error converting value "Simple" to type 'X'. Path '', line 1, position 8. ---> System.InvalidCastException: Invalid cast from 'System.String' to 'X'.
   at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
   at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType)
   --- End of inner exception stack trace ---
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)

However if I remove IConvertible from X, it works.

@ashmind
Copy link
Author

ashmind commented Oct 5, 2015

It's even more interesting with IDictionary<X, object>, which produces:

Could not convert string 'Key' to dictionary key type 'X'. Create a TypeConverter to convert from the string to the key type object.

even though TypeConverter exists and works in absence of IConvertible.

@JamesNK
Copy link
Owner

JamesNK commented Oct 12, 2015

By design. You will need to override with a JsonConverter.

@JamesNK JamesNK closed this as completed Oct 12, 2015
@ashmind
Copy link
Author

ashmind commented Oct 12, 2015

@JamesNK That's what I wanted to use, but JsonConverter is not applied to Dictionary keys -- even the message explicitly suggests TypeConverter.

@JamesNK
Copy link
Owner

JamesNK commented Oct 12, 2015

You could write a JsonConverter for the dictionary. Or you could deserialize to different type and then create the dictionary after you deserialize.

@dennisdoomen
Copy link

This really sounds like a bug. I've observed the same problem. Everything works as expected until your target object implements IConvertible. Then your type converter will be completely ignored.

@claudiobottari
Copy link

Getting the same, I just add a custom object to my app that implements IConvertible and now I can't serialize/deserialize... Currently looking for a workaround, any idea?

@webwarrior06
Copy link

Try adding [JsonObject] attribute above the class implementing the IConvertible interface.

@dkattan
Copy link

dkattan commented Aug 10, 2018

Adding [JsonObject] fixed this for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants