Как украсить JSON.NET StringEnumConverter
Я потребляю api, который возвращает строковые значения, подобные этому. some-enum-value
Я пытаюсь поместить эти значения в перечисление, поскольку по умолчанию StringEnumConverter не выполняет работу, я пытаюсь украсить этот конвертер некоторой дополнительной логикой. Как я могу убедиться, что значения правильно десериализованы?
Следующий код - это моя попытка выполнить эту работу. Однако строка reader = new JsonTextReader(new StringReader(cleaned));
разрушает все это, поскольку base.ReadJson can not распознает строку как JSON.
Есть ли лучший способ сделать это, не выполняя всю логику excisting в StringEnumConverter? Как исправить мой подход?
public class BkStringEnumConverter : StringEnumConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
var enumString = reader.Value.ToString();
if (enumString.Contains("-"))
{
var cleaned = enumString.Split('-').Select(FirstToUpper).Aggregate((a, b) => a + b);
reader = new JsonTextReader(new StringReader(cleaned));
}
}
return base.ReadJson(reader, objectType, existingValue, serializer);
}
private static string FirstToUpper(string input)
{
var firstLetter = input.ToCharArray().First().ToString().ToUpper();
return string.IsNullOrEmpty(input)
? input
: firstLetter + string.Join("", input.ToCharArray().Skip(1));
}
}
Ответы
Ответ 1
Я решил проблему, добавив атрибуты EnumMember в мои значения enum. StringEnumConverter по умолчанию в StringEnumConverter
прекрасно справляется с этими атрибутами.
Пример:
public enum MyEnum
{
[EnumMember(Value = "some-enum-value")]
SomeEnumValue,
Value,
[EnumMember(Value = "some-other-value")]
SomeOtherValue
}
Обратите внимание, что вам нужно указывать атрибуты только в случае тире или других специальных символов, которые вы не можете использовать в своем перечислении. Прописные строчные буквы обрабатываются StringEnumConverter
. Поэтому, если служба возвращает значение типа someenumvalue
вы должны использовать его в перечислении Someenumvalue
. Если вы предпочитаете SomeEnumValue
вы должны использовать атрибут EnumMember
. В случае, если сервис возвращает его как этот someEnumValue
вы можете просто использовать его как этот SomeEnumValue
(он работает сразу после использования свойства CamelCaseText).
Вы можете легко указать ваши конвертеры и другие настройки в JsonSerializerSettings
.
Вот пример настроек, которые я использую сам.
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Converters = new List<JsonConverter> { new StringEnumConverter { CamelCaseText = true } },
NullValueHandling = NullValueHandling.Ignore
};
Ответ 2
Вы также можете использовать этот код:
[JsonConverter(typeof(StringEnumConverter))]
public enum ResposeStatus
{
[EnumMember(Value = "success value")]
Success,
[EnumMember(Value = "fail value")]
Fail,
[EnumMember(Value = "error value")]
Error
};
При сериализации JsonConvert.Serialize()
будет использоваться текст внутри EnumMember
.
Ответ 3
Это стало проще в Json.NET 12.0.1 с добавлением NamingStrategy
к StringEnumConverter
:
Новая функция - добавлена поддержка NamingStrategy в StringEnumConverter
Во-первых, поскольку в Json.NET нет встроенной стратегии именования регистра, в качестве подкласса используется SnakeCaseNamingStrategy
:
public class DashCaseNamingStrategy : SnakeCaseNamingStrategy
{
protected override string ResolvePropertyName(string name)
{
return base.ResolvePropertyName(name).Replace('_', '-');
}
}
Теперь вы можете передать его в любой из нескольких конструкторов для StringEnumConverter
при создании и добавлении конвертеров в JsonSerializerSettings.Converters
:
var settings = new JsonSerializerSettings
{
Converters = { new StringEnumConverter(typeof(DashCaseNamingStrategy)) },
};
var json = JsonConvert.SerializeObject(MyEnum.SomeEnumValue, settings);
Assert.IsTrue(json == "\"some-enum-value\""); // Passes successfully
MyEnum
таком подходе для MyEnum
аннотации вообще не требуются.
Ответ 4
Также вы можете использовать следующие методы:
public static string GetDescription(this Enum member)
{
if (member.GetType().IsEnum == false)
throw new ArgumentOutOfRangeException(nameof(member), "member is not enum");
var fieldInfo = member.GetType().GetField(member.ToString());
if (fieldInfo == null)
return null;
var attributes = fieldInfo.GetCustomAttributes<DescriptionAttribute>(false).ToList();
return attributes.Any() ? attributes.FirstOrDefault()?.Description : member.ToString();
}
или
public static string GetDescription(this object member)
{
var type = member.GetType();
var attributes = type.GetCustomAttributes<DescriptionAttribute>(false).ToList();
return attributes.Any() ? attributes.FirstOrDefault()?.Description : member.GetType().Name;
}
а перечисление должно иметь атрибут дескрипции. Вот так:
public enum MyEnum
{
[Description("some-enum-value")]
And,
[Description("some-enum-value")]
Or
}
И чем вы можете использовать свой enum
следующим образом:
MyEnum.GetDescription(); //return "some-enum-value"