Newtonsoft.Json-反序列化自定义类型时,DeserializeObject引发:转换值"somestring"时出错.键入CustomType [英] Newtonsoft.Json - DeserializeObject throws when deserializing custom type: Error converting value "somestring" to type CustomType

查看:108
本文介绍了Newtonsoft.Json-反序列化自定义类型时,DeserializeObject引发:转换值"somestring"时出错.键入CustomType的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个自定义类型:

[TypeConverter(typeof(FriendlyUrlTypeConverter))]
public class FriendlyUrl : IEquatable<FriendlyUrl>, IConvertible
{
    public FriendlyUrl()
    {
        _friendlyUrl = string.Empty;
    }

    public FriendlyUrl(string value)
    {
        value = value.Trim();
        if (!FriednlyUrlValidator.Validate(value))
            throw new FriendlyUrlValidationException("Invalid value for FrienlyUrl");
        _friendlyUrl = value;
    }

    public static implicit operator FriendlyUrl(string friendlyUrlValue)
    {
        if (friendlyUrlValue != "" && !FriednlyUrlValidator.Validate(friendlyUrlValue))
            throw new FriendlyUrlValidationException($"Invalid value for FrienlyUrl: {friendlyUrlValue}");
        return new FriendlyUrl { _friendlyUrl = friendlyUrlValue };
    }

    public static implicit operator string(FriendlyUrl furl)
    {
        return furl._friendlyUrl;
    }

    public override string ToString()
    {
        return _friendlyUrl;
    }

    private string _friendlyUrl;

//[...skip IEquatable implementation...]

    TypeCode IConvertible.GetTypeCode()
    {
        return _friendlyUrl.GetTypeCode();
    }

    bool IConvertible.ToBoolean(IFormatProvider provider)
    {
        return ((IConvertible) _friendlyUrl).ToBoolean(provider);
    }

    byte IConvertible.ToByte(IFormatProvider provider)
    {
        return ((IConvertible) _friendlyUrl).ToByte(provider);
    }

    char IConvertible.ToChar(IFormatProvider provider)
    {
        return ((IConvertible) _friendlyUrl).ToChar(provider);
    }

    DateTime IConvertible.ToDateTime(IFormatProvider provider)
    {
        return ((IConvertible) _friendlyUrl).ToDateTime(provider);
    }

    decimal IConvertible.ToDecimal(IFormatProvider provider)
    {
        return ((IConvertible) _friendlyUrl).ToDecimal(provider);
    }

    double IConvertible.ToDouble(IFormatProvider provider)
    {
        return ((IConvertible) _friendlyUrl).ToDouble(provider);
    }

    short IConvertible.ToInt16(IFormatProvider provider)
    {
        return ((IConvertible) _friendlyUrl).ToInt16(provider);
    }

    int IConvertible.ToInt32(IFormatProvider provider)
    {
        return ((IConvertible) _friendlyUrl).ToInt32(provider);
    }

    long IConvertible.ToInt64(IFormatProvider provider)
    {
        return ((IConvertible) _friendlyUrl).ToInt64(provider);
    }

    sbyte IConvertible.ToSByte(IFormatProvider provider)
    {
        return ((IConvertible) _friendlyUrl).ToSByte(provider);
    }

    float IConvertible.ToSingle(IFormatProvider provider)
    {
        return ((IConvertible) _friendlyUrl).ToSingle(provider);
    }

    string IConvertible.ToString(IFormatProvider provider)
    {
        return _friendlyUrl.ToString(provider);
    }

    object IConvertible.ToType(Type conversionType, IFormatProvider provider)
    {
        if (conversionType == typeof(FriendlyUrl))
            return this;
        return ((IConvertible) _friendlyUrl).ToType(conversionType, provider);
    }

    ushort IConvertible.ToUInt16(IFormatProvider provider)
    {
        return ((IConvertible) _friendlyUrl).ToUInt16(provider);
    }

    uint IConvertible.ToUInt32(IFormatProvider provider)
    {
        return ((IConvertible) _friendlyUrl).ToUInt32(provider);
    }

    ulong IConvertible.ToUInt64(IFormatProvider provider)
    {
        return ((IConvertible) _friendlyUrl).ToUInt64(provider);
    }
}

这是Json序列化/反序列化(xUnit)的测试:

and here's the test for Json serialization/deserialization (xUnit):

    [Fact]
    public void ConvertToJsonAndBack()
    {
        FriendlyUrl friendlyUrl = "some-friendly-url-1";
        string friendlyUrlJson = JsonConvert.SerializeObject(friendlyUrl);
        Assert.Equal($"\"{friendlyUrl}\"", friendlyUrlJson);

        // ******** Throws the next line: ********
        FriendlyUrl deserialized = JsonConvert.DeserializeObject<FriendlyUrl>(friendlyUrlJson);
        Assert.Equal(friendlyUrl, deserialized);
    }

它抛出异常:

Newtonsoft.Json.JsonSerializationException:将值"some-friendly-url-1"转换为错误;键入"BlahBlah.Entities.FriendlyUrl".路径'',第1行,位置21. ---- System.InvalidCastException:从'System.String'到'BlahBlah.Entities.FriendlyUrl'的无效转换.

Newtonsoft.Json.JsonSerializationException : Error converting value "some-friendly-url-1" to type 'BlahBlah.Entities.FriendlyUrl'. Path '', line 1, position 21. ---- System.InvalidCastException : Invalid cast from 'System.String' to 'BlahBlah.Entities.FriendlyUrl'.

堆栈跟踪:

堆栈跟踪: JsonSerializerInternalReader.EnsureType(JsonReader阅读器,对象值,CultureInfo文化,JsonContract合同,类型targetType) JsonSerializerInternalReader.CreateValueInternal(JsonReader阅读器,类型objectType,JsonContract合同,JsonProperty成员,JsonContainerContract containerContract,JsonProperty containerMember,对象现存值) JsonSerializerInternalReader.Deserialize(JsonReader reader,Type objectType,Boolean checkAdditionalContent) JsonSerializer.DeserializeInternal(JsonReader reader,Type objectType) JsonSerializer.Deserialize(JsonReader reader,Type objectType) JsonConvert.DeserializeObject(字符串值,类型类型,JsonSerializerSettings设置) JsonConvert.DeserializeObject [T](字符串值,JsonSerializerSettings设置) JsonConvert.DeserializeObject [T](字符串值)

Stack Trace: JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType) JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent) JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType) JsonSerializer.Deserialize(JsonReader reader, Type objectType) JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings) JsonConvert.DeserializeObject[T](String value)

现在,如果我删除IConvertible实现并仅保留:

Now, it does work if I remove IConvertible implementation and leave only:

  • 显式和隐式' FriendlyUrl< =>字符串 "转换运算符.
  • TypeConverter实现-参见第一行.
  • explicit and implicit 'FriendlyUrl <=> string' conversion operators.
  • TypeConverter implementation - see the very first line.

但是当类实现IConvertible时,我得到了该错误.我该如何解决?

But when the class implements IConvertible I get that error. How can I fix this?

(我确实需要实现IConvertible.我也尝试使用JsonObject,它没有帮助).

(I really need IConvertible to be implemented. I also tried to use JsonObject and it didn't help).

这是复制品- https://dotnetfiddle.net/YPfr60 .

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

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        return value is string sValue ? new FriendlyUrl(sValue) : base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        return destinationType == typeof(string) ? value.ToString() : base.ConvertTo(context, culture, value, destinationType);
    }
}

推荐答案

由于您的类实现了IConvertible,因此JsonSerializerInternalReader显然正在尝试调用Convert.ChangeType,而不是使用您提供的TypeConverter.有评论

Because your class implements IConvertible, the JsonSerializerInternalReader is apparently attempting to call Convert.ChangeType instead of using the TypeConverter you supplied. There is a comment at line 984 of the source code stating that Convert.ChangeType does not work for a custom IConvertible, so the author is ostensibly aware of the issue:

if (contract.IsConvertable)
{
    JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract)contract;

    ...

    // this won't work when converting to a custom IConvertible
    return Convert.ChangeType(value, contract.NonNullableUnderlyingType, culture);
}

您可以通过为FriendlyUrl类实现自定义JsonConverter来解决此问题:

You can work around the problem by implementing a custom JsonConverter for your FriendlyUrl class:

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return new FriendlyUrl((string)reader.Value);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue(((FriendlyUrl)value).ToString());
    }
}

要使用JsonConverter,只需以与[TypeConverter]相同的方式向FriendlyUrl类添加[JsonConverter]属性.然后,可以删除[TypeConverter]属性,除非出于其他目的需要它. (Json.Net的DefaultContractResolver在解析类型时首先查找JsonConverter,因此它将优先于TypeConverter.)

To use the JsonConverter, simply add a [JsonConverter] attribute to your FriendlyUrl class in the same way that you did for [TypeConverter]. You can then remove the [TypeConverter] attribute, unless you need it for some other purpose. (Json.Net's DefaultContractResolver looks for a JsonConverter first when resolving types, so it will take precedence over TypeConverter.)

[JsonConverter(typeof(FriendlyUrlJsonConverter))]
public class FriendlyUrl : IEquatable<FriendlyUrl>, IConvertible
{
    ...
}

提琴: https://dotnetfiddle.net/HyaQWb

这篇关于Newtonsoft.Json-反序列化自定义类型时,DeserializeObject引发:转换值"somestring"时出错.键入CustomType的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆