- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
这是我的小自定义值类型类:
public struct PhoneNumber
{
private readonly string _phoneNumber;
public PhoneNumber(string phoneNumber)
{
_phoneNumber = phoneNumber;
// ... lots more will happen here to validate the phone number
}
public static implicit operator PhoneNumber(string phoneNumber)
{
return new PhoneNumber(phoneNumber);
}
public static implicit operator string(PhoneNumber phoneNumber)
{
return phoneNumber.ToString();
}
public override string ToString()
{
return _phoneNumber;
}
}
使用隐式运算符到/从字符串和重写的 ToString 方法,我希望 Json.NET 应该能够序列化和反序列化此类的实例。
但是,这样做:
var pn = new PhoneNumber("1234567890");
var json = JsonConvert.SerializeObject(pn);
... 仅返回一个空的 {}
。
我宁愿不将属性应用于我的值类型类或实现自定义序列化程序。
我是否可以遵循另一个约定来让 Json.NET 以我喜欢的方式运行?
最佳答案
C# 是强类型语言。然而,人们通常使用 string
值(value)观无处不在。在那种情况下,语言的强类型起源无济于事。此类问题称为 Primitive obsession .
.NET 提供 TypeConverter
在不同类型之间转换值。 Newtonsoft.Json
隐式使用类型转换器。
我想到了这样的解决方案:
强类型接口(interface)
首先我们定义接口(interface)来包装原始值。
/// <summary>
/// Interface to make values strongly-typed with help of TypeConverters.
/// </summary>
/// <typeparam name="TInnerType">Inner type</typeparam>
public interface IStronglyTyped<out TInnerType>
{
/// <summary>
/// Inner value.
/// </summary>
TInnerType Value { get; }
}
强类型类
然后我们定义实现该接口(interface)的类。
/// <summary>
/// Strongly-typed value based on inner type (e.g. <see cref="string"/> or <see cref="System.Uri"/>).
/// If you need validation then implement ".IsValid()" method.
/// </summary>
/// <typeparam name="TInnerType">Type of the inner value.</typeparam>
public abstract class StronglyTyped<TInnerType> : IStronglyTyped<TInnerType>
{
/// <summary>
/// Validation error format. Should contain "{0}" placeholder.
/// </summary>
protected virtual string ValidationErrorFormat => "'{0}' is not valid value";
/// <summary>
/// Inner value.
/// </summary>
public TInnerType Value { get; }
/// <inheritdoc />
protected StronglyTyped(TInnerType value)
{
Validate(value);
Value = value;
}
private void Validate(TInnerType value)
{
if (!IsValid(value)) throw new StrongTypeException(GetType(), String.Format(ValidationErrorFormat, value));
}
/// <summary>
/// Validates the value.
/// </summary>
/// <returns>'true' if value is valid.</returns>
protected virtual bool IsValid(TInnerType value)
{
return true;
}
/// <inheritdoc />
public override string ToString()
{
return Value.ToString();
}
/// <summary>
/// Checks the equality of the inner values.
/// </summary>
protected bool Equals(StronglyTyped<TInnerType> other)
{
return string.Equals(Value, other.Value);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((StronglyTyped<TInnerType>)obj);
}
/// <inheritdoc />
public override int GetHashCode()
{
return (Value != null ? Value.GetHashCode() : 0);
}
/// <summary>
/// Implicit mapping to `string`.
/// </summary>
public static implicit operator string(StronglyTyped<TInnerType> obj)
{
return obj?.ToString();
}
}
类型转换器
在我们有了接口(interface)和类之后,我们实现了一个泛型类型转换器。
这有助于将原始字符串值转换为强类型 IStronglyTyped<TValue>
.
/// <summary>
/// Generic type converter for converting `string` to `TValue` (and other way around).
/// </summary>
public class StringTypeConverter<TValue> : TypeConverter
{
/// <inheritdoc />
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(string);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value == null)
{
return null;
}
if (value is string stringValue)
{
return CreateInstance(stringValue);
}
throw new NotSupportedException($"Can't convert `{value.GetType().Name}` to `{typeof(TValue)}`");
}
/// <summary>
/// Creates instance of `TValue` from string value.
/// </summary>
protected TValue CreateInstance(string value)
{
return CreateInstanceInternal(value);
}
/// <summary>
/// Creates instance of `TValue` from string value.
/// </summary>
protected virtual TValue CreateInstanceInternal(string value)
{
if (typeof(IStronglyTyped<string>).IsAssignableFrom(typeof(TValue)))
{
return (TValue)Activator.CreateInstance(typeof(TValue), value);
}
else
{
var typeConverter = TypeDescriptor.GetConverter(typeof(TValue));
return (TValue)typeConverter.ConvertFromString(value);
}
}
/// <inheritdoc />
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
return ((TValue)value)?.ToString();
}
throw new NotSupportedException($"Can't convert `{typeof(TValue)}` to `{destinationType.Name}`");
}
}
自定义强类型类
/// <summary>
/// Phone number.
/// </summary>
[TypeConverter(typeof(StringTypeConverter<PhoneNumber>))]
public class PhoneNumber : StronglyTyped<string>
{
/// <inheritdoc />
public PhoneNumber(string value)
: base(value.Trim())
{
}
/// <inheritdoc />
protected override bool IsValid(string value)
{
if (value.Trim() == string.Empty)
{
return false;
}
// Validation logic goes here
return true;
}
}
这种方法适用于 Newtonsoft.Json
无需额外配置。
UPD
此外,如果您想将这些强类型类与 Entity Framework Core 一起使用,您需要教它如何将它们转换为 string
值。它不使用 TypeConverter
但使用 ValueConverter<,>
.
因此您需要定义一个自定义值转换器。
值转换器
public class StronglyTypedValueConverter<TStronglyTyped, TInner> : ValueConverter<TStronglyTyped, string>
where TStronglyTyped : class, IStronglyTyped<TInner>
{
private static readonly TypeConverter TYPE_CONVERTER = TypeDescriptor.GetConverter(typeof(TStronglyTyped));
public StronglyTypedValueConverter(ConverterMappingHints mappingHints = null)
: base(
stronglyTyped => FromStronglyTyped(stronglyTyped),
value => ToStronglyTyped(value),
mappingHints)
{
}
private static string FromStronglyTyped(TStronglyTyped stronglyTyped)
{
var result = TYPE_CONVERTER.ConvertToString(stronglyTyped);
return result;
}
private static TStronglyTyped ToStronglyTyped(object value)
{
var result = TYPE_CONVERTER.ConvertFrom(value);
return result as TStronglyTyped;
}
}
不幸的是,这还不够。您必须为任何自定义类型的每个属性注册自定义转换。这要棘手得多。
关于c# - Json.NET 不能仅使用隐式运算符自动处理值类型吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57598313/
我是一名优秀的程序员,十分优秀!