Sé que hay un atributo para manejar los establecedores privados, pero quiero este comportamiento como predeterminado, ¿hay alguna manera de lograrlo? Excepto modificar la fuente. Sería genial si hubiera un escenario para esto.
Respuestas:
Vine aquí buscando el atributo real que hace que Json.NET llene una propiedad de solo lectura al deserializar, y eso es simplemente [JsonProperty], por ejemplo:
[JsonProperty]
public Guid? ClientId { get; private set; }
Simplemente proporcione un constructor que tenga un parámetro que coincida con su propiedad:
public class Foo
{
public string Bar { get; }
public Foo(string bar)
{
Bar = bar;
}
}
Ahora esto funciona:
string json = "{ \"bar\": \"Stack Overflow\" }";
var deserialized = JsonConvert.DeserializeObject<Foo>(json);
Console.WriteLine(deserialized.Bar); // Stack Overflow
Prefiero este enfoque siempre que sea posible ya que:
{ get; private set; }y solo { get; }.{get;private set;}, no con{get;}
{ get; }si el tipo tiene un constructor con un parámetro que coincide con el nombre de la propiedad.
Escribí una distribución de origen NuGet para esto, que instala un solo archivo con dos solucionadores de contratos personalizados:
Instale NuGet:
Install-Package JsonNet.PrivateSettersContractResolvers.Source
Entonces solo usa cualquiera de los resolutores:
var settings = new JsonSerializerSettings
{
ContractResolver = new PrivateSetterContractResolver()
};
var model = JsonConvert.DeserializeObject<Model>(json, settings);
Puede leer sobre esto aquí: http://danielwertheim.se/json-net-private-setters-nuget/
Repositorio de GitHub: https://github.com/danielwertheim/jsonnet-privatesetterscontractresolvers
Hay dos alternativas que pueden solucionar el problema.
Alt 1: en los deserializadores
ContractResolver.DefaultMembersSearchFlags =
DefaultMembersSearchFlags | BindingFlags.NonPublic;
La opción de serialización predeterminada admite todos los tipos de miembros de clase. Por lo tanto, esta solución devolverá todos los tipos de miembros privados, incluidos los campos. Solo me interesa apoyar también a los setters privados.
Alt2: Cree un ContractResolver personalizado:
Por lo tanto, estas son las mejores opciones, ya que solo verificamos las propiedades.
public class SisoJsonDefaultContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(
MemberInfo member,
MemberSerialization memberSerialization)
{
//TODO: Maybe cache
var prop = base.CreateProperty(member, memberSerialization);
if (!prop.Writable)
{
var property = member as PropertyInfo;
if (property != null)
{
var hasPrivateSetter = property.GetSetMethod(true) != null;
prop.Writable = hasPrivateSetter;
}
}
return prop;
}
}
Para obtener más información, lea mi publicación: http://danielwertheim.se/json-net-private-setters/
DefaultMembersSearchFlagsha quedado obsoleto .
{get; }NO es equivalente a { get; private set; }. Por la primera vía property.GetSetMethod(true)regresa nully la segunda true. Esto me sorprendió. Debe tener private set;para que la deserialización funcione como se esperaba.
La respuesta de @ Daniel (Alt2) es acertada, pero necesitaba que esto funcionara tanto para los establecedores privados como para los captadores (estoy trabajando con una API que en realidad tiene algunas cosas de solo escritura, como user.password). Esto es lo que terminé con:
public class NonPublicPropertiesResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
var prop = base.CreateProperty(member, memberSerialization);
if (member is PropertyInfo pi) {
prop.Readable = (pi.GetMethod != null);
prop.Writable = (pi.SetMethod != null);
}
return prop;
}
}
Registrado así:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
ContractResolver = new NonPublicPropertiesResolver()
};