Manter invólucro ao serializar dictionarys

Eu tenho um projeto Web Api sendo configurado assim:

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

No entanto, quero que as checkboxs de dictionary permaneçam inalteradas. Existe algum atributo no Newtonsoft.Json eu possa usar para uma class para indicar que eu quero que o Newtonsoft.Json permaneça inalterado durante a serialização?

 public class SomeViewModel { public Dictionary Data { get; set; } } 

Não existe um atributo para fazer isso, mas você pode fazer isso personalizando o resolvedor.

Eu vejo que você já está usando um CamelCasePropertyNamesContractResolver . Se você derivar uma nova class de resolvedor e replace o método CreateDictionaryContract() , poderá fornecer uma function DictionaryKeyResolver substituta que não altere os nomes das chaves.

Aqui está o código que você precisa:

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

Demonstração:

 class Program { static void Main(string[] args) { Foo foo = new Foo { AnIntegerProperty = 42, HTMLString = "", Dictionary = new Dictionary { { "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 Dictionary { get; set; } } 

Aqui está a saída do acima. Observe que todos os nomes de propriedade de class são caseados em camelo, mas as chaves do dictionary mantiveram seu invólucro original.

 { "anIntegerProperty": 42, "htmlString": "", "dictionary": { "WHIZbang": "1", "FOO": "2", "Bar": "3" } } 

O Json.NET 9.0.1 introduziu a hierarquia de classs NamingStrategy para lidar com esse tipo de problema. Ele extrai a lógica para o remapeamento algorítmico de nomes de propriedade do resolvedor de contrato para uma class leve separada que permite controlar se as chaves de dictionary , os nomes de propriedade explicitamente especificados e os nomes de dados de extensão (em 10.0.1 ) são remapeados.

Usando DefaultContractResolver e definindo NamingStrategy para uma instância de CamelCaseNamingStrategy você pode gerar JSON com nomes de propriedade NamingStrategy com camel e chaves de dictionary não modificadas:

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

Notas:

  • A implementação atual do CamelCasePropertyNamesContractResolver também especifica que os membros .Net com nomes de propriedades explicitamente especificados (por exemplo, aqueles onde JsonPropertyAttribute.PropertyName foi definido) devem ter seus nomes remapeados:

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

    O resolver acima preserva esse comportamento. Se você não quiser isso, defina OverrideSpecifiedNames = false .

  • O Json.NET tem várias estratégias de nomenclatura internas, incluindo:

    1. CamelCaseNamingStrategy . Uma estratégia de nomenclatura de checkbox de camelo que contém a lógica de remapeamento de nome anteriormente incorporada em CamelCasePropertyNamesContractResolver .
    2. SnakeCaseNamingStrategy . Uma estratégia de nomeação de caso de cobra .
    3. DefaultNamingStrategy . A estratégia de nomenclatura padrão. Nomes de propriedade e chaves de dictionary permanecem inalterados.

    Ou você pode criar o seu próprio herdando da class base abstrata NamingStrategy .

  • Embora também seja possível modificar o NamingStrategy de uma instância do CamelCasePropertyNamesContractResolver , uma vez que o último compartilha informações de contrato globalmente em todas as instâncias de cada tipo , isso pode levar a efeitos colaterais inesperados se o aplicativo tentar usar várias instâncias do CamelCasePropertyNamesContractResolver . Esse problema não existe com o DefaultContractResolver , portanto, é mais seguro usar quando qualquer customização de lógica de checkbox é necessária.

Essa é uma resposta muito legal. Mas por que não apenas replace o ResolveDictionaryKey ?

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

A resposta selecionada é perfeita, mas eu acho que quando estou digitando isso, o resolvedor de contrato deve mudar para algo assim porque o DictionaryKeyResolver não existe mais 🙂

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