Reputation: 86957
We have a JSON that we can deserialize into a custom domain model, no problems at all. It includes a property which is a custom enum:
public enum UserType
{
President,
Chump
}
We've now changed our enum class but still need to accept and deserialize the previous values of any JSON's that arrive. It's like we now have two versions of our JSON
public enum UserType
{
President,
Vice-President,
Citizen // Chump maps to Citizen, now.
}
and in the json itself..
"userType": "chump"; // needs to map to Citizen
I'm not sure how to do this.
Is this using JsonConverter
?
Also, this is our custom settings we use for all our serialization and deserializtion. NOTE: we serialize any enum to it's string
description/value, not it's int
value.
internal static JsonSerializerSettings JsonSerializerSettings => new JsonSerializerSettings
{
Converters = new JsonConverter[]
{
new StringEnumConverter()
},
Formatting = Formatting.Indented
};
Upvotes: 2
Views: 3919
Reputation: 39082
You can implement a custom Converter
deriving from StringEnumConverter
:
public class UserTypeEnumConverter : StringEnumConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(UserType);
}
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
var isNullable = (Nullable.GetUnderlyingType(objectType) != null);
if (!isNullable)
{
throw new JsonSerializationException();
}
return null;
}
var token = JToken.Load(reader);
var value = token.ToString();
if (string.Equals(value, "chump", StringComparison.OrdinalIgnoreCase))
{
return UserType.Citizen;
}
else
{
return base.ReadJson(reader, objectType, existingValue, serializer);
}
}
}
The converter handles the case when the underlying type is nullable as well and is invoked only when the type of property declared on the deserialized class is UserType
. It then checks if the value being parsed is the "deprecated" one and if not, it delegates the reading to the base StringEnumConverter
.
Upvotes: 1
Reputation: 1301
In your Enum
just add the EnumMember
attribute which specify the value for serialization/deserialization process.
public enum UserType
{
President,
VicePresident,
[EnumMember(Value = "chump")]
Citizen // Chump maps to Citizen, now.
}
The property userType
will be Citizen
when, in your json, the userType
property is equal to "Chump"
or "Citizen"
.
Remember to add the System.Runtime.Serialization
reference to your project.
I noticed that the check of the Value
property of EnumMember
attribute is case-sensitive. So you can't use "Chump"
if in your json you have "chump"
. To solve this problem you can use a custom StringEnumConverter
.
public class UserTypeEnumConverter : StringEnumConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var userTypeType = typeof(UserType);
if (objectType == userTypeType)
{
var value = reader.Value.ToString().ToLower();
foreach (var item in Enum.GetNames(userTypeType))
{
var memberValue = GetEnumMemberValue(userTypeType.GetMember(item)[0]);
if (memberValue != null && memberValue.ToLower() == value)
{
return Enum.Parse(userTypeType, item);
}
}
}
return base.ReadJson(reader, objectType, existingValue, serializer);
}
}
private static string GetEnumMemberValue(MemberInfo memberInfo)
{
var attributes = memberInfo.GetCustomAttributes(typeof(EnumMemberAttribute), inherit: false);
if (attributes.Length == 0) return null;
return ((EnumMemberAttribute)attributes[0]).Value;
}
In the above code, I check only the EnumMember
attribute because the UserType
's members case-insensitive check is already done by the default StringEnumConvert
.
Note that this converter will work only for your UserType
enum beacuse of the check:
var userTypeType = typeof(UserType);
if (objectType == userTypeType)
{
Replace the JsonSerializerSettings
initialization with:
internal static JsonSerializerSettings JsonSerializerSettings => new JsonSerializerSettings
{
Converters = new JsonConverter[]
{
new UserTypeEnumConverter()
},
Formatting = Formatting.Indented
};
I assumed that Vice-President
enumerator is VicePresident
in UserType
.
Upvotes: 7