Mantenga la carcasa al serializar diccionarios

Tengo un proyecto de API web configurado de la siguiente manera:

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

Sin embargo, quiero que las mayúsculas y minúsculas del diccionario permanezcan sin cambios. ¿Hay algún atributo en Newtonsoft.Json que pueda usar en una clase para indicar que quiero que la cubierta permanezca sin cambios durante la serialización?

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

No hay un atributo para hacer esto, pero puede hacerlo personalizando el resolver.

Veo que ya está usando CamelCasePropertyNamesContractResolver . Si obtiene una nueva clase de resolución de eso y anula el método CreateDictionaryContract() , puede proporcionar una función sustituta de DictionaryKeyResolver que no cambie los nombres de las claves.

Aquí está el código que necesitaría:

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

Manifestación:

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

Aquí está el resultado de lo anterior. Tenga en cuenta que todos los nombres de las propiedades de la clase son de camel-cased, pero las claves del diccionario han conservado su carcasa original.

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

Json.NET 9.0.1 introdujo la jerarquía de clases NamingStrategy para manejar este tipo de problema. Extrae la lógica para la reasignación algorítmica de los nombres de propiedades desde el resuelve de contratos a una clase separada y liviana que permite controlar si se reasignan claves de diccionario , nombres de propiedad explícitamente especificados y nombres de datos de extensión (en 10.0.1 ).

Al usar DefaultContractResolver y al establecer NamingStrategy en una instancia de CamelCaseNamingStrategy , puede generar JSON con los nombres de las propiedades camel-cased y las claves del diccionario sin modificar:

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

Notas:

  • La implementación actual de CamelCasePropertyNamesContractResolver también especifica que los miembros de .Net con nombres de propiedades explícitamente especificados (por ejemplo, aquellos en los que se ha establecido JsonPropertyAttribute.PropertyName ) deberían tener sus nombres reasignados:

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

    La resolver anterior conserva este comportamiento. Si no quiere esto, configure OverrideSpecifiedNames = false .

  • Json.NET tiene varias estrategias de nomenclatura integradas que incluyen:

    1. CamelCaseNamingStrategy . Una estrategia de nomenclatura de camello que contiene la lógica de reasignación de nombres previamente incorporada en CamelCasePropertyNamesContractResolver .
    2. SnakeCaseNamingStrategy . Una estrategia de nombres de casos de serpientes .
    3. DefaultNamingStrategy . La estrategia de nombres predeterminada. Los nombres de propiedades y las claves del diccionario no se modifican.

    O bien, puede crear el suyo heredando de la clase base abstracta NamingStrategy .

  • Si bien también es posible modificar NamingStrategy de una instancia de CamelCasePropertyNamesContractResolver , ya que este último comparte información de contrato globalmente en todas las instancias de cada tipo , esto puede generar efectos secundarios inesperados si la aplicación intenta utilizar instancias múltiples de CamelCasePropertyNamesContractResolver . No existe tal problema con DefaultContractResolver , por lo que es más seguro usarlo cuando se requiere una personalización de la lógica de la carcasa.

Esa es una muy buena respuesta. Pero, ¿por qué no simplemente anula la ResolveDictionaryKey ?

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

La respuesta seleccionada es perfecta, pero supongo que para cuando escriba esto, el resuelve el contrato debe cambiar a algo así porque DictionaryKeyResolver ya no existe 🙂

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