0

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:

[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.
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.

11
  • 1
    You should not provide code in pictures , it is better to write it by your own as code-text. Commented Jul 9, 2020 at 12:19
  • This time I fixed it for you but the next time please make it by your own. Commented Jul 9, 2020 at 12:19
  • Looks like you want to serialize property metadata including certain attributes. You'll need reflection to inspect the class and its properties. What have you tried? Commented Jul 9, 2020 at 12:25
  • @LopDev - this isn't something you should fix (or propose fixing) - this is something the OP should do, and should be straightforward to do, with exact formatting as the original text. Your proposed edit isn't exactly the same (different spacing, for example), and could potentially introduce typos, which is why it's important for the OP to do it, and not for you to spend time typing all of their code in, based on a picture. Commented Jul 9, 2020 at 12:53
  • Please edit your question to contain properly formatted code, not images of code. Also, please see this meta post which enumerates many reasons why this is important. Commented Jul 9, 2020 at 12:54

1 Answer 1

1

Here is a rough implementation of what you need, using the .NET Core System.Text.Json

How to write custom converters for JSON serialization (marshalling) in .NET

public static class SerializationTester
{
    public static void TestSerialize()
    {
        TestClass test = new TestClass()
        {
            AnyNumber = 3,
            DateOfBirth = DateTime.UtcNow,
            Email = "[email protected]",
            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; }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.