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

Mar*_*eIV 5 c# json.net implicit-conversion

考虑这个接口和类......

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>{} 
Run Code Online (Sandbox Code Playgroud)

注意到和从的隐式转换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);
Run Code Online (Sandbox Code Playgroud)

结果是这样...

{
    "UltimateQuestion": {
        "value": "What do you get when you multiply six by nine?"
    },
    "UltimateAnswer": {
        "value": 42
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望隐式转换到和从TValue,分别是stringint,它会给我这个......

{
    "UltimateQuestion": "What do you get when you multiply six by nine?",
    "UltimateAnswer": 42
}
Run Code Online (Sandbox Code Playgroud)

那么我怎样才能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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

cku*_*uri 0

您可以编写一个转换器(请参阅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);
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我向您的界面添加了一个非泛型“值”属性,以获取值,而不管实际的泛型类型如何,而无需使用反射:

public interface IValueHolder{
   object value { get; }
}

public class ValueHolder<TValue> : IValueHolder
{
    // as before

    object IValueHolder.value => (object)value;
}
Run Code Online (Sandbox Code Playgroud)

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

var settings = new JsonSerializerSettings {
    Converters = {
        new ValueHolderJsonConverter()
    };
Run Code Online (Sandbox Code Playgroud)

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