简体   繁体   English

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

[英]Is there a way to get JSON.Net to serialize an object using implicit conversion instead of its actual type?

Consider this interface and classes... 考虑这个接口和类......

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>{} 

Note the implicit conversions to and from TValue . 请注意与TValue的隐式转换。

The point of this code is I'm trying to store key/value pairs where the value can be any type, but without having boxing penalties. 这段代码的重点是我试图存储键值/值对,其中值可以是任何类型,但没有拳击惩罚。 This is possible since I will always know the type when setting or retrieving the value from the storage. 这是可能的,因为在设置或从存储中检索值时我总是知道类型。 (That's why I didn't just use Dictionary<string, object> .) (这就是为什么我不只是使用Dictionary<string, object> 。)

Now consider this code... 现在考虑这段代码......

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);

The result is this... 结果就是......

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

I was hoping with the implicit conversion to and from TValue , which are string and int respectively, it would give me this... 我希望与TValue的隐式转换,分别是stringint ,它会给我这个......

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

So how can I get ValueStorage<T> to serialize and deserialize as T ? 那么我怎样才能让ValueStorage<T>序列化和反序列化为T I don't mind writing custom converters (provided they're generic and can be based on TValue ) or custom SettingsContractResolver subclasses, but I'm not sure where to start. 我不介意编写自定义转换器(假设它们是通用的,可以基于TValue )或自定义的SettingsContractResolver子类,但我不知道从哪里开始。

Update 更新

The more I think about this, the more I think this isn't actually solvable. 我想的越多,我就越认为这实际上是不可解决的。 This is because while serialization is easy, the deserialization would need to know the types for TValue , which aren't stored in the JSON in the format that I want, therefore it can't be deserialized. 这是因为虽然序列化很容易,但反序列化需要知道TValue的类型,它们没有以我想要的格式存储在JSON中,因此无法反序列化。 (Even the original output above with the nested objects has the same issue. You need the type information for this to work in any capacity.) (即使上面的嵌套对象的原始输出也存在同样的问题。您需要此类型信息才能以任何方式工作。)

However, as not to keep this question open, what about a converter that stores the type information for TValue instead of ValueHolder<TValue> ? 但是,为了不打开这个问题,那么存储TValue的类型信息而不是ValueHolder<TValue>的转换器呢?

eg 例如

{
    "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
    }
}

That way the converter could reconstruct ValueHolder<TValue> instances during deserialization. 这样转换器可以在反序列化期间重建ValueHolder<TValue>实例。 It's not perfect, but would at least allow proper deserialization without exposing the ValueHolder framework. 它并不完美,但至少允许在不暴露ValueHolder框架的情况下进行适当的反序列化。

You can write a converter (see https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm ). 您可以编写转换器(请参阅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);
    }
}

Please not that I added a non-generic "value" property to your interface to get the values regardless of the actual generic type without needing to use reflection: 请注意,我向您的界面添加了非泛型“value”属性,无论实际泛型类型如何都可以获取值,而无需使用反射:

public interface IValueHolder{
   object value { get; }
}

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

    object IValueHolder.value => (object)value;
}

To use it you create a new JsonSerializerSettings class (or append your existing one): 要使用它,您需要创建一个新的JsonSerializerSettings类(或附加现有的类):

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

And provide it as parameter to SerializeObject and DeserializeObject. 并将其作为SerializeObject和DeserializeObject的参数提供。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM