问题描述
我有一个自定义类型:
[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);
}
它抛出异常:
堆栈跟踪:
现在,如果我删除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
.有评论在源代码的第984行指出Convert.ChangeType
不适用于自定义IConvertible
,因此作者表面上意识到该问题:
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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!