Jon.NET custom serialization of enum type with data annotation

I want to serialize an enumeration type so that it returns an array with enumerations as an object that contains both "value", "name", and the value of the data annotation. I need help with serialization. Here is what I have done so far: Enumeration:

public enum Status { [Display(Name="Active status")] Active = 1, [Display(Name = "Deactive status")] Deactive = 2, [Display(Name = "Pending status")] Pending = 3 } 

The DTO object to be serialized:

 public class ProjectDto { public Type StatusEnum { get; set; } public Status CurrentStatus { get; set; } } 

Assignment of values:

 var project = new ProjectDto { CurrentStatus = Status.Active, StatusEnum = typeof (Status) }; var output = JsonConvert.SerializeObject(project); 

To get the values ​​from an enumeration, I use:

 Enum.GetNames(typeof(Status)) //To get the names in the enum Enum.GetValues(typeof(Status)) //To get the values in the enum 

To get the value of the data annotation name is a bit more complicated, but I found help in this article: http://geeksharp.com/2011/11/02/power-up-your-enumerations/ They created a helper method that gets the value written to data annotations using:

 public static string GetAttributeValue<T>(this Enum e, Func<T, object> selector) where T : Attribute { var output = e.ToString(); var member = e.GetType().GetMember(output).First(); var attributes = member.GetCustomAttributes(typeof(T), false); if (attributes.Length > 0) { var firstAttr = (T)attributes[0]; var str = selector(firstAttr).ToString(); output = string.IsNullOrWhiteSpace(str) ? output : str; } return output; } 

And you can get the value using:

 .GetAttributeValue<DisplayAttribute>(y => y.Name) 

The output should be something like

 { statusEnum: [ { "value": "1", "name": "Active", "label": "Active status" }, { "value": "2", "name": "Deactive", "label": "Deactive status" }, { "value": "3", "name": "Pending", "label": "Pending status" } ], currentStatus: { "value": "1", "name": "Active", "label": "Active status" } } 

As already mentioned, I need help creating custom Json.NET serialization and deserialization to get the desired result. Any help would be a priori.

+5
source share
2 answers

Well, this can probably be cleaned up a bit, but I would write two custom converters: one for the Enum type, and the other for the enum value:

I created a custom class to serialize to the final result you want:

 public class EnumValue { public int Value { get; set; } public string Name { get; set; } public string Label { get; set; } } 

As well as a static class that performs some functions for creating instances of this type from Enum and enumeration values:

 public static class EnumHelpers { public static EnumValue GetEnumValue(object value, Type enumType) { MemberInfo member = enumType.GetMember(value.ToString())[0]; DisplayAttribute attribute = member.GetCustomAttribute<DisplayAttribute>(); return new EnumValue { Value = (int)value, Name = Enum.GetName(enumType, value), Label = attribute.Name }; } public static EnumValue[] GetEnumValues(Type enumType) { Array values = Enum.GetValues(enumType); EnumValue[] result = new EnumValue[values.Length]; for (int i = 0; i < values.Length; i++) { result[i] = GetEnumValue( values.GetValue(i), enumType); } return result; } } 

Then there are two converter classes. This first serializes System.Type to the desired object:

 public class EnumTypeConverter : JsonConverter { public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer) { if (value == null) { writer.WriteNull(); return; } EnumValue[] values = EnumHelpers.GetEnumValues((Type)value); serializer.Serialize(writer, values); } public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotSupportedException(); } public override bool CanRead { get { return false; } } public override bool CanConvert(Type objectType) { return typeof(Type).IsAssignableFrom(objectType); } } 

And then there is one that serializes the actual value of the enumeration:

 public class EnumValueConverter : JsonConverter { public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer) { if (value == null) { writer.WriteNull(); return; } EnumValue result = EnumHelpers.GetEnumValue(value, value.GetType()); serializer.Serialize(writer, result); } public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotSupportedException(); } public override bool CanRead { get { return false; } } public override bool CanConvert(Type objectType) { return objectType.IsEnum; } } 

Here's how you would use it all:

 var pr = new ProjectDto(); pr.CurrentStatus = Status.Active; pr.StatusEnum = typeof(Status); var settings = new JsonSerializerSettings(); settings.Converters = new JsonConverter[] { new EnumTypeConverter(), new EnumValueConverter() }; settings.Formatting = Newtonsoft.Json.Formatting.Indented; string serialized = JsonConvert.SerializeObject(pr, settings); 

Example: https://dotnetfiddle.net/BVp7a2

+7
source

Here is the approach that I often use when I encounter the same problem. ( Here is the fiddle , if you want to go straight to the working example)

Enum Settings

I often need alternative values ​​for enumerations. For this reason, I like to create an attribute to store these alternative values. For instance:

 [AttributeUsage(AttributeTargets.Field)] public class AlternativeValueAttribute : Attribute { public string JsonValue { get; set; } public string DbValue { get; set; } // and any other kind of alternative value you need... } 

(Note that the DbValue property DbValue not relevant for the purposes of this demonstration ... It simply demonstrates the existence of several alternative values.)

And when creating an enum object, I use this attribute for every value that requires an alternative value. For instance:

 public enum ObjectState { [AlternativeValue(DbValue = "-1", JsonValue="is-unknown")] Unknown, [AlternativeValue(DbValue = "1", JsonValue="is-active")] Active, [AlternativeValue(DbValue = "0", JsonValue="is-inactive")] Inactive // ... } 

Create Converter

Now we need to create a converter so that we can use an alternative value. In this case, we serialize / deserialize Json, so we will create a JsonConverter :

 public class AlternativeValueJsonConverter<TEnum> : JsonConverter where TEnum : struct, IConvertible, IComparable, IFormattable { public override bool CanConvert( Type objectType ) { // we can only convert if the type of object matches the generic type specified return objectType == typeof( TEnum ); } public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ) { if( objectType == typeof(TEnum) ) { // cycle through the enum values foreach(var item in (TEnum[])Enum.GetValues( typeof( TEnum ) ) ) { // get the AlternativeValueAttribute, if it exists var attr = item.GetType().GetTypeInfo().GetRuntimeField( item.ToString() ) .GetCustomAttribute<AlternativeValueAttribute>(); // if the JsonValue property matches the incoming value, // return this enum value if (attr != null && attr.JsonValue == reader.Value.ToString()) { return item; } } } return null; } public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer ) { if( value.GetType() == typeof( TEnum ) ) { // cycle through the enum values foreach( var item in (TEnum[])Enum.GetValues( typeof( TEnum ) ) ) { // if we've found the right enum value if (item.ToString() == value.ToString() ) { // get the attribute from the enum value var attr = item.GetType().GetTypeInfo().GetRuntimeField( item.ToString() ) .GetCustomAttribute<AlternativeValueAttribute>(); if( attr != null) { // write out the JsonValue property value serializer.Serialize( writer, attr.JsonValue ); } } } } } } 

Usage

Usage Finally, to use this JsonConverter , we need to decorate our enum object with it. Thus, the ObjectState enumeration that we have already declared must be updated to use the converter. For instance:

 [JsonConverter(typeof(AlternativeValueJsonConverter<ObjectState>))] public enum ObjectState { [AlternativeValue(DbValue = "-1", JsonValue="is-unknown")] Unknown, [AlternativeValue(DbValue = "1", JsonValue="is-active")] Active, [AlternativeValue(DbValue = "0", JsonValue="is-inactive")] Inactive // ... } 

Now for the demo, we will create a simple POCO that contains an ObjectState enumeration and convert it to Json to make sure that we get the expected results:

 public class DemoPoco { public ObjectState MyObjectState { get; set; } } public static void Main( string[] args ) { DemoPoco demo = new DemoPoco { MyObjectState = ObjectState.Active }; var json = JsonConvert.SerializeObject( demo ); Console.WriteLine(json); // output: {"MyObjectState":"is-active"} } 
+1
source

Source: https://habr.com/ru/post/1201721/


All Articles