简体   繁体   English

Serialize C# class or .net core model with data annotation attributes to JSON format

[英]Serialize C# class or .net core model with data annotation attributes to JSON format

I know that there is a way but what is the right way to convert(serialize) .NET Core Model Class (C# class properties) from this:我知道有一种方法,但是转换(序列化).NET Core Model Class(C# ZA2F2ED4F8EBC2ABCDZ 属性)的正确方法是什么?

[Required]
[DataType(DataType.Text)]

public string Name {get;set;}
[Required, DataType(DataType.EmailAddress)]

public string Email {get;set;}
[Required]
[DataType(DataType.Text)]


public string Subject{get;set;}
[Required]
[MaxLength(500)]
[Required, DataType(DataType.MultilineText)]

public string Message{get;set;}
[Required]
[MaxLength(500)]
[Required, DataType(DataType.DateTime)]

public DateTime DateOfBirth{get;set;}

public int AnyNumber{get;set;}

To this:对此:


[
    {
        "name": 
        {
            "value": "",
            "type": "text",
            "validations": 
            {
                "required": true
            } 
        }
    },
    {
        "email": 
        {
            "value": "",
            "type": "email",
            "validations": 
            {
                "required": true
            } 
        },
        "message": 
        {
            "value": "",
            "type": "multilineText",
            "validations": 
            {
                "required": true,
                "maxLength": 500
            } 
        },
        "anyNumber": 
        {
            "value": 0,
            "type": "number",
            "validations": 
            {
                "required": false
            } 
        }

    },
]

So any class that I provide structured like in the provided snipped image to get JSON serialized response.因此,我提供的任何 class 的结构都类似于提供的截图图像,以获得 JSON 序列化响应。
I have found and tried some solutions but things for me get very complicated and I know that reflection is expensive so I have to use it smartly.我已经找到并尝试了一些解决方案,但对我来说事情变得非常复杂,而且我知道反射很昂贵,所以我必须巧妙地使用它。
Thank you in advance.先感谢您。

Here is a rough implementation of what you need, using the .NET Core System.Text.Json这是您需要的粗略实现,使用 .NET Core System.Text.Json

How to write custom converters for JSON serialization (marshalling) in .NET 如何在 .NET 中为 JSON 序列化(编组)编写自定义转换器

public static class SerializationTester
{
    public static void TestSerialize()
    {
        TestClass test = new TestClass()
        {
            AnyNumber = 3,
            DateOfBirth = DateTime.UtcNow,
            Email = "hello@world.com",
            Message = "hello world!",
            Name = "john smith",
            Subject = "some subject"
        };
        var options = new JsonSerializerOptions()
        {
            WriteIndented = true,
            IgnoreNullValues = true
        };
        options.Converters.Add(new MetadataConverter());
        var serialized = JsonSerializer.Serialize(test, options);
        Console.WriteLine(serialized);
    }
}

public class MetadataConverter : JsonConverterFactory
{
    /// <summary>
    /// contain nul metadata for types that don't have metdata attributes and dont' need custom converters
    /// each type will only be parsed once with reflections, obviously the attribute values are identical for all class instances
    /// </summary>
    private Dictionary<Type, Dictionary<PropertyInfo, Metadata>> typesMetadataCache = new Dictionary<Type, Dictionary<PropertyInfo, Metadata>>();
    public override bool CanConvert(Type typeToConvert)
    {
        Dictionary<PropertyInfo, Metadata> typeMeta;
        if (!typesMetadataCache.TryGetValue(typeToConvert, out typeMeta))
        {
            typesMetadataCache[typeToConvert] = typeMeta = GetTypeMeta(typeToConvert);
        }
        return typeMeta != null;
    }

    private Dictionary<PropertyInfo, Metadata> GetTypeMeta(Type typeToConvert)
    {
        Dictionary<PropertyInfo, Metadata> theReturn = new Dictionary<PropertyInfo, Metadata>();
        bool metadataSpecified = false;
        foreach (var currentProperty in typeToConvert.GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            var required = currentProperty.GetCustomAttributes<RequiredAttribute>()?.FirstOrDefault();
            var dataType = currentProperty.GetCustomAttributes<DataTypeAttribute>()?.FirstOrDefault();
            var maxLength = currentProperty.GetCustomAttributes<MaxLengthAttribute>()?.FirstOrDefault();
            if (required != null || dataType != null || maxLength != null)
            {
                metadataSpecified = true;
            }

            var currentMeta = theReturn[currentProperty] = new Metadata()
            {
                type = dataType?.DataType.ToString() ?? currentProperty.PropertyType.Name,
                validations = new Validations()
                {
                    maxLength = maxLength?.MaxLength,
                    required = (required != null)
                }
            };
        }
        if (metadataSpecified)
        {
            return theReturn;
        }
        // metadata not specified for any property, don't use custom converter
        return null;
    }

    public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
    {
        return (JsonConverter)Activator.CreateInstance(typeof(MetadataTypeConverter<>).MakeGenericType(typeToConvert), options, typesMetadataCache[typeToConvert]);
    }

    private class MetadataTypeConverter<TValue> : JsonConverter<TValue>
    {
        private Dictionary<PropertyInfo, JsonConverter> propertyConverters = new Dictionary<PropertyInfo, JsonConverter>();
        public MetadataTypeConverter(JsonSerializerOptions options, Dictionary<PropertyInfo, Metadata> typeMetadata)
        {
            foreach (var currentMeta in typeMetadata)
            {
                if (currentMeta.Value == null)
                {
                    propertyConverters[currentMeta.Key] = options.GetConverter(currentMeta.Key.PropertyType);
                }
                else
                {
                    propertyConverters[currentMeta.Key] = (JsonConverter)Activator.CreateInstance(typeof(MetadataValueConverter<>).MakeGenericType(currentMeta.Key.PropertyType), options, currentMeta.Value);
                }
            }
        }
        public override TValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            throw new NotImplementedException();
        }

        public override void Write(Utf8JsonWriter writer, TValue value, JsonSerializerOptions options)
        {
            writer.WriteStartObject();
            foreach (var currentConverter in propertyConverters)
            {
                var currentPropertyValue = currentConverter.Key.GetValue(value);
                if (currentConverter.Value is IMetadataValueConverter currentMetadataValueConverter)
                {
                    currentMetadataValueConverter.Write(writer, currentPropertyValue, options);
                }
                else
                {
                    var currentWriteMethod = currentConverter.Value.GetType().GetMethod("Write", BindingFlags.Public | BindingFlags.Instance);
                    currentWriteMethod.Invoke(currentConverter.Value, new object[] { writer, currentPropertyValue, options });
                }
            }
            writer.WriteEndObject();
        }
    }
    private class MetadataValueConverter<TValue> : JsonConverter<TValue>, IMetadataValueConverter
    {
        private Metadata metadata;
        public MetadataValueConverter(JsonSerializerOptions options, Metadata metadata)
        {
            this.metadata = metadata;
        }
        public override TValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            var value = JsonSerializer.Deserialize<MetadataWithValue>(ref reader, options);
            return value.value;
        }

        public override void Write(Utf8JsonWriter writer, TValue value, JsonSerializerOptions options)
        {
            JsonSerializer.Serialize(writer, new MetadataWithValue(metadata, value), options);
        }
        void IMetadataValueConverter.Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) => Write(writer, (TValue)value, options);
        public class MetadataWithValue : Metadata
        {
            public MetadataWithValue() { }
            public MetadataWithValue(Metadata metadata, TValue value)
            {
                type = metadata.type;
                validations = metadata.validations;
                this.value = value;
            }
            public TValue value { get; set; }
        }
    }

    private interface IMetadataValueConverter
    {
        void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options);
    }
    private class Metadata
    {
        public string type { get; set; }
        public Validations validations { get; set; }
    }
    private class Validations
    {
        public bool required { get; set; }
        public int? maxLength { get; set; }

    }
}

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class RequiredAttribute : Attribute { }
public enum DataType
{
    Text,
    EmailAddress,
    MultilineText,
    DateTime
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class DataTypeAttribute : Attribute
{
    public DataTypeAttribute(DataType dataType)
    {
        DataType = dataType;
    }

    public DataType DataType { get; private set; }
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class MaxLengthAttribute : Attribute
{
    public MaxLengthAttribute(int maxLength)
    {
        MaxLength = maxLength;
    }

    public int MaxLength { get; private set; }
}
public class TestClass
{
    [Required]
    [DataType(DataType.Text)]
    public string Name { get; set; }

    [Required, DataType(DataType.EmailAddress)]
    public string Email { get; set; }

    [Required]
    [DataType(DataType.Text)]
    public string Subject { get; set; }

    [Required]
    [MaxLength(500)]
    [Required, DataType(DataType.MultilineText)]
    public string Message { get; set; }

    [Required]
    [MaxLength(500)]
    [Required, DataType(DataType.DateTime)]
    public DateTime DateOfBirth { get; set; }

    public int AnyNumber { get; set; }
}

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

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