How to apply a general rule to reassign all property names when serializing using Json.NET?

When deserializing a Json object in a .Net type , if the field names do not match, I find that you can decorate your type properties [JsonProperty(PropertyName = "name")]

This is fine and dandy for several properties that do not match, but is there a way to set an agreement or a rule?

Json

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

WITH#

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

I have many fields with similar names that I would like to find out if there is a way to say to set a rule to always remove spaces (EG: Job Type -> JobType ) and replace # with Number (for example: Job # -> JobNumber )?

It seems like a custom ContractResolver might be the only solution, but I can't figure out how to use it to tear out spaces and replace "#" with "Number". Does anyone have a reference example?

Or, I hope, there will be a nice simple solution that I missed.

PS We also accept offers for a better name.

+5
source share
2 answers

Assuming you are working with Json.NET 9.0.1 or later, this can be done using custom NamingStrategy . For example, here, based on SnakeCaseNamingStrategy and StringUtils.ToSnakeCase() 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 string ResolvePropertyName(string name) { return SpaceWords(name); } enum WordState { Start, Lower, Upper, NewWord } static string SpaceWords(string s) { // Adapted from StringUtils.ToSnakeCase() // https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Utilities/StringUtils.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 // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, 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 (string.IsNullOrEmpty(s)) { return s; } StringBuilder sb = new StringBuilder(); 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.ToString(); } } 

Then you can apply it to your type as follows:

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

And the generated JSON will look like this:

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

Notes:

+2
source

Yes, ContractResolver is the way to go.

The problem is that they only work with the target property on the source, i.e. "JobType" -> "Job Type" , and not as you would like. This makes the solution a little rougher than you might want.

First we make our ContractResolver , inherit from DefaultContractResolver , so all this works fine except for the bit we want to configure:

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

Then in our deserialization, we set the ContractResolver to JsonSerializerSettings :

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

Source: https://habr.com/ru/post/1272207/


All Articles