【问题标题】:Json.Net: Serialize/Deserialize property as a value, not as an objectJson.Net:将属性序列化/反序列化为值,而不是对象
【发布时间】:2017-03-21 16:48:22
【问题描述】:

在另一个类中使用时,如何实现 Id 类的以下 JSON 表示?

class Car
{
    public StringId Id { get; set; }
    public string Name { get; set; }
}

class StringId
{
    public string Value { get; set; }
}

// ---------------------------------------------

// Desired representation
{ "Id": "someId", "Name": "Ford" }

// Default (undesired) representation
{ "Id" : { "Value": "someId" }, "Name": "Ford" }

【问题讨论】:

    标签: c# json serialization json.net


    【解决方案1】:

    您可以为StringId 添加TypeConverter。 Json.NET 将获取类型转换器并使用它来将其从字符串转换为字符串:

    [TypeConverter(typeof(StringIdConverter))]
    class StringId
    {
        public string Value { get; set; }
    }
    
    class StringIdConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
                return true;
            return base.CanConvertFrom(context, sourceType);
        }
    
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(StringId))
                return true;
            return base.CanConvertTo(context, destinationType);
        }
    
        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            if (value is string)
            {
                return new StringId { Value = (string)value };
            }
            return base.ConvertFrom(context, culture, value);
        }
    
        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(string) && value is StringId)
            {
                return ((StringId)value).Value;
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }
    }
    

    如果您的字符串表示包含嵌入的数字或日期/时间数据,请务必使用传入的 culture 而不是默认的当前文化转换该数据。 Json.NET 将使用正确的文化 which is the invariant culture by default 调用转换器,从而确保生成的 JSON 文件可在文化之间移植。

    示例fiddle

    但是请注意,如果您使用的是 .Net Core,则仅在 Json.NET 10.0.1 之后才添加了对类型转换器的支持。自 10.0.3 起,Json.NET Portable 版本不支持类型转换器。

    或者,如果您不介意将 Json.NET 特定属性添加到您的类型,您可以使用 custom JsonConverter

    [JsonConverter(typeof(StringIdConverter))]
    class StringId
    {
        public string Value { get; set; }
    }
    
    class StringIdConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(StringId);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
                return null;
            var token = JToken.Load(reader);
            return new StringId { Value = (string)token };
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var id = (StringId)value;
            writer.WriteValue(id.Value);
        }
    }
    

    您也可以在global settings中设置转换器。

    示例fiddle

    【讨论】:

    • 这些方法都不适合我。我想知道.NET Core 是否会影响序列化。我写了一个单元测试,我在调试器中看到的是没有调用转换器的方法......
    • 更正:我在测试中不是很小心... JsonConverter 确实有效,但我更喜欢 TypeConverter。
    • @IgorSoloydenko - 你使用的是什么版本的 json.net?显然它不适用于 8.0.3:github.com/dotnet/corefx/issues/8679
    【解决方案2】:

    你可以重写StringId类的ToString方法来返回值

        public override string ToString()
        {
            return this.Value;
        }
    

    稍后您将需要 TypeConverter 将字符串反序列化为 StringId

    public class StringIdConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
            {
                return true;
            }
            return base.CanConvertFrom(context, sourceType);
        }
    
    
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            if (value is string)
            {
                return new StringId(value.ToString());
            }
            return base.ConvertFrom(context, culture, value);
        }
    }
    

    并用这个属性装饰你的StringId

    [TypeConverter(typeof(StringIdConverter))]
    public class StringId{
        ...
    }
    

    【讨论】:

    • 只是我的好奇心。使用 TypeConverter 是否会严重影响序列化性能?
    • 我已经在生产环境中使用它 3 年了,从未影响过性能
    猜你喜欢
    • 2014-08-22
    • 2021-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多