Setters privados em Json.Net

93

Eu sei que há um atributo para lidar com setters privados, mas eu meio que quero esse comportamento como padrão. Existe uma maneira de fazer isso? Exceto ajustar a fonte. Seria ótimo se houvesse uma configuração para isso.

Daniel
fonte
1
Eu estava procurando por esta ou aquela resposta.
marbel82,

Respostas:

112

Vim aqui procurando o atributo real que faz o Json.NET preencher uma propriedade somente leitura ao desserializar, e isso é simplesmente [JsonProperty], por exemplo:

[JsonProperty]
public Guid? ClientId { get; private set; }

Solução alternativa

Basta fornecer um construtor que tenha um parâmetro que corresponda à sua propriedade:

public class Foo
{
    public string Bar { get; }

    public Foo(string bar)
    {
        Bar = bar;
    }
}

Agora isso funciona:

string json = "{ \"bar\": \"Stack Overflow\" }";

var deserialized = JsonConvert.DeserializeObject<Foo>(json);
Console.WriteLine(deserialized.Bar); // Stack Overflow

Prefiro essa abordagem sempre que possível, pois:

  • Não exige que você decore suas propriedades com atributos.
  • Funciona com ambos { get; private set; }e justo { get; }.
Saeb Amini
fonte
19
Apenas uma pequena nota: funciona com {get;private set;}, não com{get;}
tymtam
8
Apenas uma pequena atualização. Agora também funciona com {get;};
Hav
1
@Hav Qual é a versão desde então? Acabei de testar a v11.0.2 e ela não funciona {get;}
tymtam
1
@tymtam Acho que só funciona { get; }se o tipo tiver um construtor com um parâmetro correspondente ao nome da propriedade.
Saeb Amini
2
@tymtam atualizou a resposta com esta alternativa e um exemplo.
Saeb Amini
77

Atualizado, nova resposta

Eu escrevi um NuGet de distribuição de origem para isso, que instala um único arquivo com dois resolvedores de contrato personalizados:

  • PrivateSetterContractResolver
  • PrivateSetterCamelCasePropertyNamesContractResolver

Instale o NuGet:

Install-Package JsonNet.PrivateSettersContractResolvers.Source

Em seguida, basta usar qualquer um dos resolvedores:

var settings = new JsonSerializerSettings
{
    ContractResolver = new PrivateSetterContractResolver()
};

var model = JsonConvert.DeserializeObject<Model>(json, settings);

Você pode ler sobre isso aqui: http://danielwertheim.se/json-net-private-setters-nuget/

Repo do GitHub: https://github.com/danielwertheim/jsonnet-privatesetterscontractresolvers

Resposta antiga (ainda válida)

Existem duas alternativas que podem resolver o problema.

Alt 1: nos desserializadores

ContractResolver.DefaultMembersSearchFlags =
                             DefaultMembersSearchFlags | BindingFlags.NonPublic;

A opção de serialização padrão oferece suporte a todos os tipos de membros da classe. Portanto, esta solução retornará todos os tipos de membros privados, incluindo campos. Só estou interessado em também oferecer suporte a setters privados.

Alt2: Crie um ContractResolver personalizado:

Portanto, esta é a melhor opção, já que apenas verificamos as propriedades.

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 obter mais informações, leia minha postagem: http://danielwertheim.se/json-net-private-setters/

Daniel
fonte
1
@Jafin url está morto, danielwertheim.wordpress.com/2010/11/06/… o tem agora
Chris Marisic
1
Parece que Alt 2 é definitivamente o caminho a percorrer hoje em dia. DefaultMembersSearchFlagsfoi descontinuado .
Todd Menier
4
Com c # 6, {get; }NÃO é equivalente a { get; private set; }. Para o primeiro caminho property.GetSetMethod(true)retorna nulle o último true. Isso me surpreendeu. Você deve ter private set;para que a desserialização funcione conforme o esperado.
emragins
Parece que o pacote de instalação JsonNet.ContractResolvers deve ser usado agora. github.com/danielwertheim/jsonnet-contractresolvers
Alinhado em
14

A resposta de @Daniel (Alt2) está correta, mas eu precisava que isso funcionasse para setters e getters privados (estou trabalhando com uma API que, na verdade, tem algumas coisas somente para gravação, como user.password .) Aqui está o que eu terminei:

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 assim:

JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
    ContractResolver = new NonPublicPropertiesResolver()
};
Todd Menier
fonte