c# .net json.net

c# - JSON.net no debe usar valores predeterminados para los parámetros del constructor, debe usar el valor predeterminado para las propiedades



(1)

Cuando Json.NET encuentra un objeto sin un constructor sin parámetros pero con un constructor parametrizado, llamará a ese constructor para crear el objeto, haciendo coincidir los nombres de las propiedades JSON con los argumentos del constructor por nombre usando la reflexión a través de un algoritmo de mejor coincidencia entre mayúsculas y minúsculas. Es decir, una propiedad cuyo nombre también aparece en el constructor se establecerá a través de la llamada del constructor, no del método establecido (incluso si hay uno).

Por lo tanto, puede marcar un argumento constructor según sea necesario marcando la propiedad equivalente según sea necesario :

public class Dog { public Dog(int age) { this.Age = age; } [JsonProperty(Required = Required.Always)] public int Age { get; } }

Ahora JsonConvert.DeserializeObject<Dog>(jsonString) se lanzará cuando falta la propiedad "age" .

Dado que esto es algo que siempre quiere, puede crear un resuelve de contratos personalizado heredando de DefaultContractResolver o CamelCasePropertyNamesContractResolver que marque las propiedades pasadas al constructor como se requiere automáticamente, sin la necesidad de atributos:

public class ConstructorParametersRequiredContractResolver : DefaultContractResolver { // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons. // http://www.newtonsoft.com/json/help/html/ContractResolver.htm // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance." // See also https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information static ConstructorParametersRequiredContractResolver instance; static ConstructorParametersRequiredContractResolver() { instance = new ConstructorParametersRequiredContractResolver(); } public static ConstructorParametersRequiredContractResolver Instance { get { return instance; } } protected override JsonProperty CreatePropertyFromConstructorParameter(JsonProperty matchingMemberProperty, ParameterInfo parameterInfo) { var property = base.CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo); if (property != null && matchingMemberProperty != null) { var required = matchingMemberProperty.Required; if (required == Required.Default) { if (matchingMemberProperty.PropertyType != null && (matchingMemberProperty.PropertyType.IsValueType && Nullable.GetUnderlyingType(matchingMemberProperty.PropertyType) == null)) { required = Required.Always; } else { required = Required.AllowNull; } // It turns out to be necessary to mark the original matchingMemberProperty as required. property.Required = matchingMemberProperty.Required = required; } } return property; } }

Entonces

var settings = new JsonSerializerSettings { ContractResolver = ConstructorParametersRequiredContractResolver.Instance }; JsonConvert.DeserializeObject<T>(jsonString, settings)

Will una vez más lanzar.

(Tenga en cuenta que esto solo funciona si hay una propiedad correspondiente. No parece haber una manera directa de marcar un parámetro de constructor sin la propiedad correspondiente según sea necesario).

¿Hay alguna manera de decirle a JSON.net que cuando intenta deserializar utilizando un constructor (si no hay un constructor predeterminado), NO debe asignar un valor predeterminado a los parámetros del constructor y que solo debería llamar a un constructor si cada parámetro del constructor es representado en la cadena JSON? Este mismo serializador DEBERÍA usar valores predeterminados al llamar a los establecedores de propiedad / campo, la regla solo tiene un alcance para los constructores. Ninguno de los valores enum aquí parece ser apropiado: http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_DefaultValueHandling.htm

La solución NO debe depender de la aplicación de atributos a los tipos que están siendo deserializados.

por ejemplo, la cadena json "{}" se deserializará a un objeto de tipo Dog estableciendo la edad del Perro en 0 (el valor predeterminado para un int). Me gustaría una solución generalizada, no basada en atributos para evitar que esto suceda. En este caso, {"age":4} funcionaría porque la age se especifica en la cadena JSON y corresponde al parámetro constructor.

public class Dog { public Dog(int age) { this.Age = age; } public int Age { get; } }

Sin embargo, si se especifica Dog como tal, entonces "{}" debería deserializar a un perro con edad == 0, porque el perro no se está creando con un constructor.

public class Dog { public int Age { get; set; } }

Y para evitar cualquier pregunta sobre "por qué querrías hacer esto" ... Los objetos con constructores son típicamente cualitativamente diferentes a los POCO en lo que se refiere a sus propiedades. Usar un constuctor para almacenar valores de propiedad en lugar de propiedades configurables en un POCO generalmente significa que desea validar / restringir los valores de propiedad. Por lo tanto, es razonable no permitir la deserialización con valores predeterminados en presencia de constuctor (es).