代码之家  ›  专栏  ›  技术社区  ›  Mark A. Donohoe

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

  •  3
  • Mark A. Donohoe  · 技术社区  · 7 年前

    考虑这个接口和类…

    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 ,它们是 string 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
        }
    }
    

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

    1 回复  |  直到 7 年前
        1
  •  0
  •   ckuri    7 年前

    您可以编写一个转换器(请参见 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和DeserializeObject。