繁体   English   中英

我可以在Json序列化器的模型的单个属性上使用TypeConverter属性吗?

[英]Can I use TypeConverter attribute on single property of a model for Json serializer?

public class JsonModel
{
    [TypeConverter(typeof(CidNumberConvertor))]
    [JsonProperty("cid_number")]
    public Cid CidNumber;

    [TypeConverter(typeof(CidHexaConvertor))]
    [JsonProperty("cid_hexa")]
    public Cid CidHexa;

    [JsonProperty("cid_default")]
    public Cid CidDefault;
}

想象一下,我有3个字段,所有字段均为Cid类型。 我已经在全球范围内注册TypeConvertor CidHexaConvertor 看来TypeConvertor属性在属性本身上被忽略,仅当在类/模型本身上定义时才被调用。 CidHexaConvertor具有将字符串转换为Cid并将Cid转换为字符串的方法。 我以后可以共享更多代码,但是似乎无法实现这样的属性。 有什么线索吗?

在Json.NET中,并非开箱即用地检查应用于成员的[TypeConverter(typeof(...))]属性。 但是,您可以创建一个包装了任意TypeConverter自定义JsonConverter ,然后使用JsonConverterAttribute将其应用于模型。

首先,定义以下JsonConverter

public class TypeConverterJsonConverter : JsonConverter
{
    readonly TypeConverter converter;

    public TypeConverterJsonConverter(Type typeConverterType) : this((TypeConverter)Activator.CreateInstance(typeConverterType)) { }

    public TypeConverterJsonConverter(TypeConverter converter)
    {
        if (converter == null)
            throw new ArgumentNullException();
        this.converter = converter;
    }

    public override bool CanConvert(Type objectType)
    {
        return converter.CanConvertFrom(typeof(string)) && converter.CanConvertTo(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var tokenType = reader.SkipComments().TokenType;
        if (tokenType == JsonToken.Null)
            return null;
        if (!tokenType.IsPrimitive())
            throw new JsonSerializationException(string.Format("Token {0} is not primitive.", tokenType));
        var s = (string)JToken.Load(reader);
        return converter.ConvertFrom(null, CultureInfo.InvariantCulture, s);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var s = converter.ConvertToInvariantString(value);
        writer.WriteValue(s);
    }
}

public static partial class JsonExtensions
{
    public static JsonReader SkipComments(this JsonReader reader)
    {
        while (reader.TokenType == JsonToken.Comment && reader.Read())
            ;
        return reader;
    }

    public static bool IsPrimitive(this JsonToken tokenType)
    {
        switch (tokenType)
        {
            case JsonToken.Integer:
            case JsonToken.Float:
            case JsonToken.String:
            case JsonToken.Boolean:
            case JsonToken.Undefined:
            case JsonToken.Null:
            case JsonToken.Date:
            case JsonToken.Bytes:
                return true;
            default:
                return false;
        }
    }
}

然后将其应用于您的模型,如下所示:

public class JsonModel
{
    [JsonConverter(typeof(TypeConverterJsonConverter), typeof(CidNumberConvertor))]
    [TypeConverter(typeof(CidNumberConvertor))]
    [JsonProperty("cid_number")]
    public Cid CidNumber;

    [JsonConverter(typeof(TypeConverterJsonConverter), typeof(CidHexaConvertor))]
    [TypeConverter(typeof(CidHexaConvertor))]
    [JsonProperty("cid_hexa")]
    public Cid CidHexa;

    [JsonProperty("cid_default")]
    public Cid CidDefault;
}

笔记:

  • 应用JsonConverter会覆盖对Cid使用全局默认TypeConverter使用。

  • 所述JsonConverterAttribute(Type,Object[])构造用于将特定传递TypeConverter类型的构造TypeConverterJsonConverter作为参数。

  • 在生产代码中,我假设这些是属性而不是字段。

在这里示例小提琴#1。 (在没有mcve的情况下,我必须创建Cid的存根实现。)

另外,如果您有许多属性要在序列化为JSON时使用已应用的TypeConverter ,则可以创建一个自定义ContractResolver来自动实例化并应用TypeConverterJsonConverter

public class PropertyTypeConverterContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        if (property.Converter == null)
        {
            // Can more than one TypeConverterAttribute be applied to a given member?  If so,
            // what should we do?
            var attr = property.AttributeProvider.GetAttributes(typeof(TypeConverterAttribute), false)
                .OfType<TypeConverterAttribute>()
                .SingleOrDefault();
            if (attr != null)
            {
                var typeConverterType = GetTypeFromName(attr.ConverterTypeName, member.DeclaringType.Assembly);
                if (typeConverterType != null)
                {
                    var jsonConverter = new TypeConverterJsonConverter(typeConverterType);
                    if (jsonConverter.CanConvert(property.PropertyType))
                    {
                        property.Converter = jsonConverter;
                        // MemberConverter is obsolete or removed in later versions of Json.NET but
                        // MUST be set identically to Converter in earlier versions.
                        property.MemberConverter = jsonConverter;
                    }
                }
            }
        }

        return property;
    }

    static Type GetTypeFromName(string typeName, Assembly declaringAssembly)
    {
        // Adapted from https://referencesource.microsoft.com/#System/compmod/system/componentmodel/PropertyDescriptor.cs,1c1ca94869d17fff
        if (string.IsNullOrEmpty(typeName))
        {
            return null;
        }

        Type typeFromGetType = Type.GetType(typeName);

        Type typeFromComponent = null;
        if (declaringAssembly != null)
        {
            if ((typeFromGetType == null) ||
                (declaringAssembly.FullName.Equals(typeFromGetType.Assembly.FullName)))
            {
                int comma = typeName.IndexOf(',');
                if (comma != -1)
                    typeName = typeName.Substring(0, comma);
                typeFromComponent = declaringAssembly.GetType(typeName);
            }
        }

        return typeFromComponent ?? typeFromGetType;
    }
}

然后按以下方式使用它:

// Cache statically for best performance.
var resolver = new PropertyTypeConverterContractResolver();
var settings = new JsonSerializerSettings
{
    ContractResolver = resolver,
};

var json = JsonConvert.SerializeObject(root, Formatting.Indented, settings);

var root2 = JsonConvert.DeserializeObject<JsonModel>(json, settings);

笔记:

在这里样本小提琴2。

暂无
暂无

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

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