如何在使用Json.NET进行序列化时应用重新映射所有属性名称的一般规则?

将Json对象反序列化为.Net type ,如果字段名称不匹配,我发现您可以使用[JsonProperty(PropertyName = "name")]来装饰type的属性

这对于几个不匹配的属性来说很好,但是有没有办法设置约定或规则?

JSON

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

C#

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

我有很多字段使用相似的名称,我想弄清楚,有没有办法告诉设置规则总是删除空格(EG: Job Type -> JobType )并用#替换# (例如: Job # -> JobNumber )?

看起来自定义ContractResolver可能是唯一的解决方案,但我似乎无法弄清楚如何使用它来取出空格并将“#”替换为“Number”。 有没有人有参考例子?

或者,我希望有一个很好的简单解决方案,我忽略了。

PS也接受建议更好的标题。

假设您正在使用Json.NET 9.0.1或更高版本,可以使用自定义NamingStrategy来完成。 例如,这里有一个基于SnakeCaseNamingStrategy和一个James Newton-King的StringUtils.ToSnakeCase()

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

然后您可以将其应用于您的类型,如下所示:

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

生成的JSON如下:

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

笔记:

  • 如果希望策略应用于已通过JsonPropertyAttribute.PropertyName指定的属性名称的属性,请设置NamingStrategy.OverrideSpecifiedNames == true

  • 要将命名策略应用于所有类型而不是在每个对象上设置它,可以在DefaultContractResolver.NamingStrategy设置命名策略,然后在JsonSerializerSettings.ContractResolver设置合同解析器。

  • 命名策略从c#属性名称映射到JSON属性名称,反之亦然。 因此,您需要插入空格而不是“将它们拔出”并将“Number”替换为“#”。 然后由合同解析器缓存映射,并在反序列化期间完成反向查找。

是的, ContractResolver是要走的路。

问题是这些似乎只能从目标属性到源,即"JobType" -> "Job Type" ,而不是您想要的其他方式。 这使得解决方案比您想要的更加片状。

首先我们创建ContractResolver ,inheritance自DefaultContractResolver ,所以除了我们想要定制的位之外,它们都正常工作:

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

然后在我们的反序列化中,我们在JsonSerializerSettings设置ContractResolver

 static void Main(string[] args) { string 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); }