Organizadores privados en Json.Net

93

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.

Daniel
fuente
1
Estaba buscando esta o aquella respuesta.
marbel82

Respuestas:

112

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

Solución alternativa

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:

  • No requiere que decores tus propiedades con atributos.
  • Funciona con ambos { get; private set; }y solo { get; }.
Saeb Amini
fuente
19
Solo una pequeña nota: funciona con {get;private set;}, no con{get;}
tymtam
8
Solo una pequeña actualización. Ahora también funciona con {get;};
Hav
1
@Hav ¿Qué versión es desde entonces? Acabo de probar v11.0.2 y no funciona {get;}
tymtam
1
@tymtam Creo que solo funciona { get; }si el tipo tiene un constructor con un parámetro que coincide con el nombre de la propiedad.
Saeb Amini
2
@tymtam actualizó la respuesta con esta alternativa y un ejemplo.
Saeb Amini
77

Nueva respuesta actualizada

Escribí una distribución de origen NuGet para esto, que instala un solo archivo con dos solucionadores de contratos personalizados:

  • PrivateSetterContractResolver
  • PrivateSetterCamelCasePropertyNamesContractResolver

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

Respuesta anterior (aún válida)

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/

Daniel
fuente
2
Enlazando a su publicación daniel.wertheim.se/2010/11/06/json-net-private-setters
Jafin
1
@Jafin url está muerta, danielwertheim.wordpress.com/2010/11/06/… la tiene ahora
Chris Marisic
1
Parece que Alt 2 es definitivamente el camino a seguir hoy en día. DefaultMembersSearchFlagsha quedado obsoleto .
Todd Menier
4
Con c # 6, {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.
emragins
Parece que Install-Package JsonNet.ContractResolvers debería usarse ahora. github.com/danielwertheim/jsonnet-contractresolvers
Alineado el
14

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()
};
Todd Menier
fuente