У меня есть проект Web Api, настроенный следующим образом:
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
Однако я хочу, чтобы корпус словарных ключей оставался неизменным. Есть ли в Newtonsoft.Json
классе какой-либо атрибут, который я могу использовать для обозначения того, что я хочу, чтобы корпус оставался неизменным во время сериализации?
public class SomeViewModel
{
public Dictionary<string, string> Data { get; set; }
}
Ответы:
Для этого нет атрибута, но вы можете сделать это, настроив преобразователь.
Я вижу, что вы уже используете
CamelCasePropertyNamesContractResolver
. Если вы унаследовали от этого новый класс преобразователя и переопределили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; } }
Вот результат вышеизложенного. Обратите внимание, что все имена свойств класса имеют верблюжий регистр, но ключи словаря сохранили свой исходный регистр.
{ "anIntegerProperty": 42, "htmlString": "<html></html>", "dictionary": { "WHIZbang": "1", "FOO": "2", "Bar": "3" } }
источник
contract.DictionaryKeyResolver = key => key;
работает нормально.DictionaryKeyResolver
только если у моего свойства Dictionary есть какой-то настраиваемый атрибут?Json.NET 9.0.1 представил
NamingStrategy
иерархию классов для решения такого рода проблем. Он извлекает логику для алгоритмического переназначения имен свойств из преобразователя контрактов в отдельный облегченный класс, который позволяет контролировать, будут ли переназначены ключи словаря , явно указанные имена свойств и имена данных расширения (в 10.0.1 ).Используя
DefaultContractResolver
и установивNamingStrategy
экземпляр,CamelCaseNamingStrategy
вы можете сгенерировать JSON с именами свойств в верблюжьем регистре и неизмененными ключами словаря, установив его вJsonSerializerSettings.ContractResolver
:var resolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy { ProcessDictionaryKeys = false, OverrideSpecifiedNames = true } }; config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = resolver;
Примечания:
Текущая реализация
CamelCasePropertyNamesContractResolver
также указывает, что члены .Net с явно указанными именами свойств (например, те,JsonPropertyAttribute.PropertyName
которые были установлены) должны иметь переназначение своих имен:public CamelCasePropertyNamesContractResolver() { NamingStrategy = new CamelCaseNamingStrategy { ProcessDictionaryKeys = true, OverrideSpecifiedNames = true }; }
Вышеупомянутое
resolver
сохраняет такое поведение. Если вы этого не хотите, установитеOverrideSpecifiedNames = false
.Json.NET имеет несколько встроенных стратегий именования, в том числе:
CamelCaseNamingStrategy
. Стратегия именования кейсов верблюда, которая содержит ранее встроенную логику переопределения именCamelCasePropertyNamesContractResolver
.SnakeCaseNamingStrategy
. Змея случае стратегия именования.DefaultNamingStrategy
. Стратегия именования по умолчанию. Имена свойств и ключи словаря не изменились.Или вы можете создать свой собственный, унаследовав от абстрактного базового класса
NamingStrategy
.Хотя также возможно изменить
NamingStrategy
экземпляр экземпляраCamelCasePropertyNamesContractResolver
, поскольку последний использует информацию о контракте глобально для всех экземпляров каждого типа , это может привести к неожиданным побочным эффектам, если ваше приложение пытается использовать несколько экземпляровCamelCasePropertyNamesContractResolver
. Такой проблемы не существуетDefaultContractResolver
, поэтому его безопаснее использовать, когда требуется настройка логики корпуса.источник
public Dictionary<string, Dictionary<string, string>> Values { get; set; }
. Он по-прежнему использует camelCase для ключей внутреннего словаря.CamelCasePropertyNamesContractResolver
. В основномNamingStrategy
для первого будет влиять на контракты, генерируемые вторым. Это может быть то, что вы видите. Вместо этого попробуйте новую рекомендацию и дайте мне знать, если решит вашу проблему.NamingStrategy
, чтобы он мог разбирать как случай верблюда, так и случай паскаля?config
должно быть в исходном примере кода ?config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
. Похоже, это веб-API MVC 4HttpConfiguration
, см. Как установить пользовательские параметры JsonSerializerSettings для Json.NET в веб-API MVC 4? .Это очень хороший ответ. Но почему бы просто не переопределить
ResolveDictionaryKey
?class CamelCaseExceptDictionaryResolver : CamelCasePropertyNamesContractResolver { #region Overrides of DefaultContractResolver protected override string ResolveDictionaryKey(string dictionaryKey) { return dictionaryKey; } #endregion }
источник
Выбранный ответ идеален, но я думаю, что к тому времени, когда я его наберу, преобразователь контракта должен измениться на что-то вроде этого, потому что DictionaryKeyResolver больше не существует :)
public class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver { protected override JsonDictionaryContract CreateDictionaryContract(Type objectType) { JsonDictionaryContract contract = base.CreateDictionaryContract(objectType); contract.PropertyNameResolver = propertyName => propertyName; return contract; } }
источник
DictionaryKeyResolver
был добавлен в версии 7.0.1 иPropertyNameResolver
помечен как устаревший.