Сериализовать C# класс или. net базовую модель с атрибутами аннотации данных в формат JSON - PullRequest
0 голосов
/ 09 июля 2020

Я знаю, что есть способ, но каков правильный способ преобразования (сериализации). NET Класс модели ядра (C# свойства класса) из этого:

[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;}

К этому:


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

    },
]

Итак, любой класс, который я предоставляю, структурирован, как в предоставленном фрагменте изображения, чтобы получить JSON сериализованный ответ.
Я нашел и попробовал некоторые решения, но все для меня становится очень сложным, и я знаю это отражение стоит дорого, поэтому я должен использовать его с умом.
Заранее спасибо.

1 Ответ

0 голосов
/ 11 июля 2020

Вот примерная реализация того, что вам нужно, с использованием. NET Core System.Text. Json

Как написать собственные преобразователи для JSON сериализации (маршалинга) в . NET

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; }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...