序列化字典时保持封套

我有一个Web Api项目configuration像这样:

config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 

不过,我想让字典的键盘保持不变。 在Newtonsoft.Json有没有任何属性,我可以用一个类来表示,我想套pipe在序列化过程中保持不变?

 public class SomeViewModel { public Dictionary<string, string> Data { get; set; } } 

这没有一个属性,但你可以通过自定义parsing器来完成。

我看到你已经在使用一个CamelCasePropertyNamesContractResolver 。 如果从中派生一个新的parsing器类并重写CreateDictionaryContract()方法,则可以提供一个替代的不会更改键名称的DictionaryKeyResolver函数。

这是您需要的代码:

 class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver { protected override JsonDictionaryContract CreateDictionaryContract(Type objectType) { JsonDictionaryContract contract = base.CreateDictionaryContract(objectType); contract.DictionaryKeyResolver = propertyName => propertyName; return contract; } } 

演示:

 class Program { static void Main(string[] args) { Foo foo = new Foo { AnIntegerProperty = 42, HTMLString = "<html></html>", Dictionary = new Dictionary<string, string> { { "WHIZbang", "1" }, { "FOO", "2" }, { "Bar", "3" }, } }; JsonSerializerSettings settings = new JsonSerializerSettings { ContractResolver = new CamelCaseExceptDictionaryKeysResolver(), Formatting = Formatting.Indented }; string json = JsonConvert.SerializeObject(foo, settings); Console.WriteLine(json); } } class Foo { public int AnIntegerProperty { get; set; } public string HTMLString { get; set; } public Dictionary<string, string> Dictionary { get; set; } } 

这是上面的输出。 请注意,所有的类属性名称都是驼峰式的,但字典键保留了原来的shell。

 { "anIntegerProperty": 42, "htmlString": "<html></html>", "dictionary": { "WHIZbang": "1", "FOO": "2", "Bar": "3" } } 

Json.NET 9.0.1引入了NamingStrategy类层次结构来处理这类问题。 它将合约parsing器中属性名称的algorithm重新映射逻辑提取到一个单独的轻量级类别,该类别允许控制是否重新映射字典键 , 显式指定的属性名称和扩展名数据名称 (在10.0.1中 )。

通过使用DefaultContractResolver并将NamingStrategy设置为CamelCaseNamingStrategy一个实例,您可以使用驼峰式属性名称和未修改的字典键生成JSON:

 var resolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy { ProcessDictionaryKeys = false, OverrideSpecifiedNames = true } }; config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = resolver; 

笔记:

  • CamelCasePropertyNamesContractResolver的当前实现还指定具有明确指定属性名称(例如,已经设置了JsonPropertyAttribute.PropertyName属性)的.Net成员应该重新映射其名称:

     public CamelCasePropertyNamesContractResolver() { NamingStrategy = new CamelCaseNamingStrategy { ProcessDictionaryKeys = true, OverrideSpecifiedNames = true }; } 

    上面的resolver保留了这种行为。 如果你不想要这个,设置OverrideSpecifiedNames = false

  • Json.NET有几个内置的命名策略,包括:

    1. CamelCaseNamingStrategy 。 骆驼案例命名策略包含以前embedded在CamelCasePropertyNamesContractResolver中的名称重新映射逻辑。
    2. SnakeCaseNamingStrategy 。 蛇案命名策略。
    3. DefaultNamingStrategy 。 默认的命名策略。 属性名称和字典密钥不变。

    或者,您可以通过从抽象基类NamingStrategyinheritance来创build自己的。

  • 虽然也可以修改CamelCasePropertyNamesContractResolver实例的NamingStrategy ,因为后者在每个types的所有实例间全局共享合约信息 ,如果您的应用程序尝试使用CamelCasePropertyNamesContractResolver多个实例,则可能会导致意外的副作用。 DefaultContractResolver不存在这样的问题,所以当需要任何定制的套pipe逻辑时使用它是更安全的。

这是一个非常好的答案。 但是,为什么不直接重写ResolveDictionaryKey

 class CamelCaseExceptDictionaryResolver : CamelCasePropertyNamesContractResolver { #region Overrides of DefaultContractResolver protected override string ResolveDictionaryKey(string dictionaryKey) { return dictionaryKey; } #endregion } 

选定的答案是完美的,但我想我input这个时,合约parsing器必须改变这样的事情,因为DictionaryKeyResolver不存在了:)

 public class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver { protected override JsonDictionaryContract CreateDictionaryContract(Type objectType) { JsonDictionaryContract contract = base.CreateDictionaryContract(objectType); contract.PropertyNameResolver = propertyName => propertyName; return contract; } }