I want to serialize an enum type, so that it returns a array with the enums as a object that contains both the "value", "name" and a data annotation value. I need help with the serialization. Here's what I've done so far: The enum:
public enum Status
{
[Display(Name="Active status")]
Active = 1,
[Display(Name = "Deactive status")]
Deactive = 2,
[Display(Name = "Pending status")]
Pending = 3
}
The DTO Object that should be serialized:
public class ProjectDto
{
public Type StatusEnum { get; set; }
public Status CurrentStatus { get; set; }
}
Assigning of values:
var project = new ProjectDto
{
CurrentStatus = Status.Active,
StatusEnum = typeof (Status)
};
var output = JsonConvert.SerializeObject(project);
To get the values from the enum 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 data annotation name value is a bit trickier but I found help in this article: http://geeksharp.com/2011/11/02/power-up-your-enumerations/ They've created a helper method that will get the value written in the data annotation 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)
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 mentioned I need help creating the custom Json.NET serialize and deserialize to get the desired output. Any help would be apriciated.
In C#, JSON serialization very often needs to deal with enum objects. By default, enums are serialized in their integer form. This often causes a lack of interoperability with consumer applications because they need prior knowledge of what those numbers actually mean.
To serialize an enum constant, ObjectOutputStream writes the value returned by the enum constant's name method. To deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the deserialized constant is then obtained by calling the java. lang. Enum.
All you have to do is create a static method annotated with @JsonCreator in your enum. This should accept a parameter (the enum value) and return the corresponding enum. This method overrides the default mapping of Enum name to a json attribute .
Ok, this can probably be cleaned up a bit, but I would write two custom converters: one for the Enum
type, and another for the enum value:
I created a custom class to serialize into the end result that 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 does some of the legwork for creating instances of that type from Enum
s and enum 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 one serializes System.Type
into the object you wanted:
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's the one that serializes the actual enum value:
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 all of it:
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
Here's the approach I often take when faced with the same problem. (Here's a fiddle, if you want to jump straight to a working example)
I often find myself needing alternative values for enums. For this reason, I like to create an attribute to store these alternative values. For example:
[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, the DbValue
property is irrelevant for the purpose of this demonstration... It's just to demonstrate holding multiple alternative values.)
And when building my enum object's, I use this attribute on each value that requires an alternative value. For example:
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 we need to create a converter to be able to utilise the alternative value. In this case, we're serialising/deserialising Json, so we'll 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's value
serializer.Serialize( writer, attr.JsonValue );
}
}
}
}
}
}
Lastly, to use this JsonConverter
, we need to decorate our enum object with it. So the ObjectState
enum we've already declared should be updated to use the converter. For example:
[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 demonstration purposes, we'll create a simple POCO that contains the ObjectState
enum and convert it to Json to make sure 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"}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With