序列化字典时保持封套
我有一个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有几个内置的命名策略,包括:
-
CamelCaseNamingStrategy
。 骆驼案例命名策略包含以前embedded在CamelCasePropertyNamesContractResolver
中的名称重新映射逻辑。 -
SnakeCaseNamingStrategy
。 蛇案命名策略。 -
DefaultNamingStrategy
。 默认的命名策略。 属性名称和字典密钥不变。
或者,您可以通过从抽象基类
NamingStrategy
inheritance来创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; } }