Cómo deserializar un objeto que tiene propiedades con set privados utilizando Json.Net

jueves, 23 de octubre de 2014



Json.Net es una de las librerías más utilizadas para tratar con serialización y deserialización en formato Json. Hay veces que necesitamos tener el set de una propiedad privado por el negocio de nuestra aplicación. Por defecto Json.Net va a serializar el objeto correctamente porque el objeto tiene la propiedad pública, pero al hacer la deserialización la propiedad que tiene el set privado no va a ser escrita porque por defecto deben ser los set públicos para poder escribirlas.


Imaginemos que tenemos modelo como este

public class Person
{
    public Person(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }
    public DateTime UpdatedDate { get; private set; }

    public void ChangeName(string name)
    {
        Name = name;
        UpdatedDate = DateTime.UtcNow;
    }
}


Ahora vamos a crear el objeto, le cambiamos la propiedad name y lo serializamos. Si intentamos deserializar en otro objeto, el valor de la propiedad UpdatedDate no sera asignado y tendrá el valor por defecto.
Person person = new Person("Jorge");

person.ChangeName("Xurxo");

string json = JsonConvert.SerializeObject(person);

//json = {"Name":"Xurxo","UpdatedDate":"2014-10-21T19:26:00.3539087Z"}

Person person2 = JsonConvert.DeserializeObject(json);

Método atributo JsonProperty

El primer método que vamos a ver para poder deserializar objetos con propiedades con el set privado es usar el atributo JsonProperty. Tenemos que decorar la propiedad con el atributo y entonces la propiedad permite escritura por parte de Json.net.
public class Person
{
    public Person(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }

    [JsonProperty]
    public DateTime UpdatedDate { get; private set; }

    public void ChangeName(string name)
    {
        Name = name;
        UpdatedDate = DateTime.UtcNow;
    }
}


Tendríamos que decorar cada una de las propiedades con el set privado donde queremos permitir escritura a Json.Net.

Y después serializando de la misma forma el ejemplo anterior, la propiedad UpdatedDate se crea correctamente.

Método ContractResolver personalizado

El segundo método que vamos a ver es crearnos un ContractResolver personalizado heredando de DefaultContractResolver, vamos a sobre escribir la función CreateProperty y vamos a indicar que la propiedad permite escritura si se puede escribir y esto sucederá aunque sea un set privado.

public class PrivateSetterContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var prop = base.CreateProperty(member, memberSerialization);

        if (!prop.Writable)
        {
            var property = member as PropertyInfo;
            if (property != null)
            {
                prop.Writable = property.CanWrite;
            }
        }

        return prop;
    }
}


Con este método la serialización cambia un poco, ya que vamos a tener que indicarle al método Deserialize un parámetro JsonSerializerSettings que contiene nuestro Contract Resolver personalizado.
Person person2 = JsonConvert.DeserializeObject<Person>(json, new JsonSerializerSettings() { ContractResolver = new PrivateSetterContractResolver()});  

Resumen

En este post hemos visto como podemos deseserializar propiedades que tienen el set privado utilizando la libreria Json.Net.

El primer método que hemos visto ha sido el uso del atributo JsonProperty, que es simple de aplicar pero acopla nuestro modelo a la librería Json.Net, además en una clase con muchas propiedades de este tipo puede ensuciar nuestro modelo.

Y el segundo método es crear un Contract Resolver personalizado, donde sobreescribimos la función CreateProperty para indicar que se puede escribir aunque el set sea privado.

Como siempre con estas cosas el método adecuado depende de las circunstancias pero a mi el segundo método me parece en general más limpio y más reutilizable, además no acopla el modelo a Json.Net.

No hay comentarios:

Publicar un comentario