Comment appliquer une règle générale pour remapper tous les noms de propriétés lors de la sérialisation avec Json.NET?

Lors de la désérialisation d’un object Json dans un type .Net, si les noms de champ ne correspondent pas, je peux vous [JsonProperty(PropertyName = "name")] décorer les propriétés de votre type avec [JsonProperty(PropertyName = "name")]

Cela convient parfaitement pour quelques propriétés qui ne correspondent pas, mais existe-t-il un moyen de définir une convention ou une règle?

Json

 { "Job": [ { "Job #": "1", "Job Type": "A", } ] } 

C #

  [JsonProperty(PropertyName = "Job Type")] public ssortingng JobType { get; set; } [JsonProperty(PropertyName = "Job #")] public ssortingng JobNumber { get; set; } 

Ce que je voudrais savoir, c’est un moyen de définir une règle pour toujours supprimer les espaces (Exemple: Job Type -> JobType ) et remplacer # par Number (par exemple: Job # -> JobNumber )?

Il semble qu’un ContractResolver personnalisé pourrait être la seule solution, mais je n’arrive pas à comprendre comment l’utiliser pour supprimer des espaces et remplacer “#” par “Number”. Quelqu’un at-il un exemple de référence?

Ou bien, j’espère qu’il y a une solution simple et agréable que j’ai négligée.

PS Accepter également les suggestions pour un meilleur titre.

En supposant que vous travailliez avec Json.NET 9.0.1 ou une version ultérieure, vous pouvez le faire avec une NamingStrategy personnalisée. Par exemple, voici un exemple basé sur SnakeCaseNamingStrategy et SsortingngUtils.ToSnakeCase() de James Newton-King:

 public class CustomNamingStrategy : NamingStrategy { public CustomNamingStrategy(bool processDictionaryKeys, bool overrideSpecifiedNames) { ProcessDictionaryKeys = processDictionaryKeys; OverrideSpecifiedNames = overrideSpecifiedNames; } public CustomNamingStrategy(bool processDictionaryKeys, bool overrideSpecifiedNames, bool processExtensionDataNames) : this(processDictionaryKeys, overrideSpecifiedNames) { ProcessExtensionDataNames = processExtensionDataNames; } public CustomNamingStrategy() { } protected override ssortingng ResolvePropertyName(ssortingng name) { return SpaceWords(name); } enum WordState { Start, Lower, Upper, NewWord } static ssortingng SpaceWords(ssortingng s) { // Adapted from SsortingngUtils.ToSnakeCase() // https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Utilities/SsortingngUtils.cs#L191 // // Copyright (c) 2007 James Newton-King // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // ressortingction, including without limitation the rights to use, // copy, modify, merge, publish, dissortingbute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. char wordBreakChar = ' '; if (ssortingng.IsNullOrEmpty(s)) { return s; } SsortingngBuilder sb = new SsortingngBuilder(); WordState state = WordState.Start; for (int i = 0; i < s.Length; i++) { if (s[i] == ' ') { if (state != WordState.Start) { state = WordState.NewWord; } } else if (char.IsUpper(s[i])) { switch (state) { case WordState.Upper: bool hasNext = (i + 1 < s.Length); if (i > 0 && hasNext) { char nextChar = s[i + 1]; if (!char.IsUpper(nextChar) && nextChar != ' ') { sb.Append(wordBreakChar); } } break; case WordState.Lower: case WordState.NewWord: sb.Append(wordBreakChar); break; } sb.Append(s[i]); state = WordState.Upper; } else if (s[i] == wordBreakChar) { sb.Append(wordBreakChar); state = WordState.Start; } else { if (state == WordState.NewWord) { sb.Append(wordBreakChar); } sb.Append(s[i]); state = WordState.Lower; } } sb.Replace("Number", "#"); return sb.ToSsortingng(); } } 

Ensuite, vous pouvez l’appliquer à votre type comme suit:

 [JsonObject(NamingStrategyType = typeof(CustomNamingStrategy))] public class RootObject { public ssortingng JobType { get; set; } public ssortingng JobNumber { get; set; } public int JobItemCount { get; set; } public ssortingng ISOCode { get; set; } public ssortingng SourceXML { get; set; } } 

Et le JSON généré sera comme suit:

 { "Job Type": "job type", "Job #": "01010101", "Job Item Count": 3, "ISO Code": "ISO 9000", "Source XML": "c:\temp.xml" } 

Remarques:

  • Si vous souhaitez que la stratégie s’applique aux propriétés pour lesquelles des noms de propriétés sont déjà spécifiés via JsonPropertyAtsortingbute.PropertyName , définissez NamingStrategy.OverrideSpecifiedNames == true .

  • Pour appliquer votre stratégie de dénomination à tous les types plutôt que de la définir pour chaque object, vous pouvez définir la stratégie de dénomination dans DefaultContractResolver.NamingStrategy , puis définir le résolveur de contrat dans JsonSerializerSettings.ContractResolver .

  • La stratégie de dénomination mappe du nom de la propriété c # au nom de la propriété JSON, et non l’inverse. Ainsi, vous devez insérer des espaces plutôt que “les arracher” et remplacer “Numéro” par “#”. Le mappage est ensuite mis en cache par le résolveur de contrat et une recherche inversée est effectuée lors de la désérialisation.

Oui, un ContractResolver est la voie à suivre.

Le problème est que ceux-ci ne semblent fonctionner que de la propriété de destination à la source, c’est-à-dire "JobType" -> "Job Type" , et non l’inverse. Cela rend la solution un peu plus complexe que vous ne le souhaiteriez.

Premièrement, nous créons notre ContractResolver , héritant de DefaultContractResolver , de sorte que tout fonctionne normalement, à l’exception du bit que nous souhaitons personnaliser:

 public class JobContractResolver : DefaultContractResolver { protected override ssortingng ResolvePropertyName(ssortingng propertyName) { // first replace all capital letters with space then letter ("A" => " A"). This might include the first letter, so sortingm the result. ssortingng result = Regex.Replace(propertyName, "[AZ]", x => " " + x.Value).Trim(); // now replace Number with a hash result = result.Replace("Number", "#"); return result; } } 

Ensuite, lors de notre désérialisation, nous JsonSerializerSettings ContractResolver dans JsonSerializerSettings :

 static void Main(ssortingng[] args) { ssortingng input = @"{""Job #"": ""1"", ""Job Type"": ""A""}"; var job1 = JsonConvert.DeserializeObject(input, new JsonSerializerSettings { ContractResolver = new JobContractResolver() }); Console.WriteLine("JobType: {0}", job1.JobType); Console.WriteLine("JobNumber: {0}", job1.JobNumber); }