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

时间:2018-08-13 06:27:26

标签: 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>{} 

请注意与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
    }
}

我希望与TValuestring分别进行往返int的隐式转换,这会给我这个...

{
    "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框架的情况下允许适当的反序列化。

1 个答案:

答案 0 :(得分: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);
    }
}

请不要在我的接口上添加一个非泛型的“值”属性以获取值,而与实际的泛型类型无关,而无需使用反射:

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和DeserializeObject。