gpt4 book ai didi

c# - Json.NET 不能仅使用隐式运算符自动处理值类型吗?

转载 作者:行者123 更新时间:2023-11-30 15:51:19 29 4
gpt4 key购买 nike

这是我的小自定义值类型类:

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/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com