web-dev-qa-db-fra.com

Comment décorer JSON.NET StringEnumConverter

Je consomme une api qui renvoie des valeurs de chaîne comme ceci. some-enum-value

J'essaie de mettre ces valeurs dans une énumération, car le défaut StringEnumConverter ne fait pas le travail. J'essaie de décorer ce convertisseur avec une logique supplémentaire. Comment puis-je m'assurer que les valeurs sont correctement désérialisées?

Le code suivant est mon essai pour effectuer ce travail. Cependant, la ligne reader = new JsonTextReader(new StringReader(cleaned)); casse tout depuis la base.ReadJson ne peut pas reconnaître la chaîne en tant que JSON.

Existe-t-il un meilleur moyen de le faire sans avoir à implémenter toute la logique existante dans StringEnumConverter? Comment réparer mon approche?

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));
    }
}
25
Marco Franssen

J'ai résolu le problème en ajoutant des attributs EnumMember à mes valeurs enum. La variable StringEnumConverter par défaut de Json.NET gère parfaitement ces attributs.

Exemple:

public enum MyEnum
{
    [EnumMember("some-enum-value")]
    SomeEnumValue,
    Value,
    [EnumMember("some-other-value")]
    SomeOtherValue
}

Veuillez noter que vous devez uniquement spécifier les attributs en cas de tirets ou d'autres caractères spéciaux que vous ne pouvez pas utiliser dans votre enum. La majuscule minuscule est traitée par la variable StringEnumConverter. Ainsi, si le service renvoie une valeur telle que someenumvalue, vous devez l’utiliser de la sorte dans l’énumération Someenumvalue. Si vous préférez SomeEnumValue, vous devez utiliser l'attribut EnumMember. Si le service le renvoie comme ceci someEnumValue, vous pouvez simplement l'utiliser comme ceci SomeEnumValue (Il fonctionne par défaut lorsque vous utilisez la propriété CamelCaseText). 

Vous pouvez facilement spécifier vos convertisseurs et d’autres paramètres dans la variable JsonSerializerSettings.

Voici un exemple des paramètres que j'utilise moi-même.

new JsonSerializerSettings
{
    ContractResolver = new CamelCasePropertyNamesContractResolver(),
    Converters = new List<JsonConverter> { new StringEnumConverter { CamelCaseText = true } },
    NullValueHandling = NullValueHandling.Ignore
};
52
Marco Franssen

Vous pouvez également utiliser ce code:

[JsonConverter(typeof(StringEnumConverter))]
public enum ResposeStatus
{
    [EnumMember(Value = "success value")]
    Success,
    [EnumMember(Value = "fail value")]
    Fail,
    [EnumMember(Value = "error value")]
    Error
};

Lors de la sérialisation de JsonConvert.Serialize(), utilisera le texte à l'intérieur de EnumMember.

21
A-Sharabiani

Cela a été facilité dans Json.NET 12.0.1 avec l’ajout de NamingStrategy à StringEnumConverter :

Nouvelle fonctionnalité - Ajout du support de NamingStrategy à StringEnumConverter

Premièrement, étant donné qu’il n’existe pas de stratégie de nommage cas/dash intégrée dans Json.NET, définissez-en une comme suit en sous-classant SnakeCaseNamingStrategy :

public class DashCaseNamingStrategy : SnakeCaseNamingStrategy
{
    protected override string ResolvePropertyName(string name)
    {
        return base.ResolvePropertyName(name).Replace('_', '-');
    }
}

Vous pouvez maintenant le transmettre à l’un des constructeurs pour StringEnumConverter lors de la construction et de l’ajout de convertisseurs à 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

Aucune annotation n'est requise pour MyEnum avec cette approche.

0
dbc

Aussi, vous pouvez utiliser ces méthodes:

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();
        }

ou

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;
        }

et enum devrait avoir description attribut. Comme ça: 

public enum MyEnum
    {
        [Description("some-enum-value")]
        And,
        [Description("some-enum-value")]
        Or

    }

Et que vous pouvez utiliser votre enum comme ceci:

MyEnum.GetDescription(); //return "some-enum-value"
0
Maksym Labutin