有没有办法让 JSON.Net 使用隐式转换而不是其实际类型序列化对象?



考虑这个接口和类...

public interface IValueHolder{}
public class ValueHolder<TValue> : IValueHolder {
public ValueHolder(TValue value) => this.value = value;
public TValue value { get; }
public static implicit operator ValueHolder<TValue>(TValue value) => new ValueHolder<TValue>(value);
public static implicit operator TValue(ValueHolder<TValue> valueHolder) => valueHolder.value;
}
class ValueStorage : Dictionary<string, IValueHolder>{} 

请注意与TValue之间的隐式转换。

这段代码的重点是我尝试存储键/值对,其中值可以是任何类型的,但没有装箱惩罚。这是可能的,因为我在设置或从存储中检索值时将始终知道类型。(这就是为什么我不只是使用Dictionary<string, object>

现在考虑此代码...

var storage = new ValueStorage();
storage["UltimateQuestion"] = new ValueHolder<string>("What do you get when you multiply six by nine?");
storage["UltimateAnswer"] = new ValueHolder<int>(42);
var jsonSerializerSettings = new JsonSerializerSettings{
TypeNameHandling  = TypeNameHandling.None,
Formatting        = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore
};
var jsonString = JsonConvert.SerializeObject(storage, jsonSerializerSettings);

结果是这样的...

{
"UltimateQuestion": {
"value": "What do you get when you multiply six by nine?"
},
"UltimateAnswer": {
"value": 42
}
}

我希望通过与TValue的隐式转换,分别是stringint,它会给我这个......

{
"UltimateQuestion": "What do you get when you multiply six by nine?",
"UltimateAnswer": 42
}

那么我怎样才能让ValueStorage<T>序列化和反序列化为T呢? 我不介意编写自定义转换器(前提是它们是通用的并且可以基于TValue(或自定义SettingsContractResolver子类,但我不确定从哪里开始。

更新

我越想这个问题,就越觉得这实际上无法解决。 这是因为虽然序列化很容易,但反序列化需要知道TValue的类型,这些类型没有以我想要的格式存储在 JSON 中,因此无法反序列化。(即使是上面带有嵌套对象的原始输出也存在相同的问题。您需要类型信息才能以任何身份工作。

但是,为了不让这个问题保持开放,那么存储TValue类型信息而不是ValueHolder<TValue>的转换器呢?

例如

{
"UltimateQuestion": {
"$type": "[Whatever TValue is--string here]",
"value": "What do you get when you multiply six by nine?"
},
"UltimateAnswer": {
"$type": "[Whatever TValue is--int here]",
"value": 42
}
}

这样,转换器就可以在反序列化期间重建ValueHolder<TValue>实例。 它并不完美,但至少允许在不公开 ValueHolder 框架的情况下进行适当的反序列化。

你可以编写一个转换器(见 https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm(。

public class ValueHolderJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, ((IValueHolder)value).value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return Activator.CreateInstance(objectType, serializer.Deserialize(reader));
}
public override bool CanConvert(Type objectType)
{
return typeof(IValueHolder).IsAssignableFrom(objectType);
}
}

请注意,我在您的接口中添加了一个非泛型"value"属性来获取值,而不考虑实际的泛型类型,而无需使用反射:

public interface IValueHolder{
object value { get; }
}
public class ValueHolder<TValue> : IValueHolder
{
// as before
object IValueHolder.value => (object)value;
}

要使用它,请创建一个新的 JsonSerializerSettings 类(或附加现有类(:

var settings = new JsonSerializerSettings {
Converters = {
new ValueHolderJsonConverter()
};

并将其作为参数提供给 SerializeObject 和 UnserializeObject。

最新更新