Cómo heredar el atributo de la interfaz al objeto al serializarlo usando JSON.NET [duplicado]

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using Newtonsoft.Json.Serialization;
using System.Linq;
using System.Reflection;
public interface IParent
{
    [JsonProperty]
    int Id {get;set;}
}

[JsonObject(MemberSerialization.OptIn)]
public class Parent : IParent
{
    public int Id { get;set; }  
    public string Name {get;set;}   
}

public class Serializer
{
    public static void Main()
    {

        var parent = new Parent() { Id = 1, Name ="Parent"};        
        var sb = new StringBuilder();
                var sw = new StringWriter(sb);

                var settings = new JsonSerializerSettings()
                       {
                           NullValueHandling = NullValueHandling.Ignore                            
                       };

            var output = JsonConvert.SerializeObject(parent, Formatting.None, settings);
                Console.WriteLine(output);
            Console.ReadKey();
    }
}

En el código anterior, la salida es {}. ¿Es posible serializar y obtener la salida como {"Id": 1}?

preguntado el 10 de mayo de 11 a las 13:05

Apoyo a [JsonProperty] en las declaraciones de propiedades de la interfaz se ha implementado a partir de Json.NET 4.0.3, así que ahora este código simplemente funciona, consulta dotnetfiddle.net/bEIWEF. Como tal, ahora es un duplicado de Obtener SerializeObject para usar el "nombre" de JsonProperty definido dentro de la interfaz. -

2 Respuestas

Esta es una mala idea.

Habiendo dicho eso, Newtonsoft proporciona una forma de cambiar lo que está serializado: en este caso, subclase DefaultContractResolver y anular CreateProperty.

El problema es que no es fácil decidir cuándo debe optar por la serialización en función de los atributos de una interfaz. Por un lado, una clase podría potencialmente implementar múltiples interfaces con instrucciones de serialización en conflicto. Es más, deserialización un objeto en una variable declarada como una interfaz conflictiva (por ejemplo) no funcionará. Es frágil y no es seguro (permite que el código externo especifique qué datos revela una instancia).

Si tiene que hacerlo, el siguiente código funciona para su caso:

public class InterfaceContractResolver : DefaultContractResolver, IContractResolver
{
    public InterfaceContractResolver() : this(false) { }
    public InterfaceContractResolver(bool shareCache) : base (shareCache) {}

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        var interfaces = member.DeclaringType.GetInterfaces();
        foreach (var @interface in interfaces)
        {
            foreach (var interfaceProperty in @interface.GetProperties())
            {
                // This is weak: among other things, an implementation 
                // may be deliberately hiding an interface member
                if (interfaceProperty.Name == member.Name && interfaceProperty.MemberType == member.MemberType)
                {
                    if (interfaceProperty.GetCustomAttributes(typeof(JsonPropertyAttribute), true).Any())
                    {
                        property.Ignored = false;
                        return property;
                    }
                }
            }
        }
        return property;
    }

}

Luego, cuando esté creando su serializador, páselo una instancia de su resolutor en la configuración:

var settings = new JsonSerializerSettings()
{
    NullValueHandling = NullValueHandling.Ignore,
    ContractResolver = new InterfaceContractResolver(true)
};

contestado el 11 de mayo de 11 a las 07:05

Gracias Jeff, estaba buscando algo similar. - Gopal

Siempre me sorprende la flexibilidad de Json.NET. Es bueno saber que si alguna vez necesito atascar para forzar que esto funcione, puedo hacerlo. - Frank

Una forma de hacer que miles sean menos volátiles sería crear un atributo SerializeAs que te obligue a especificar solo una interfaz. Eso o un escaneo previo en busca de conflictos. - George R

Vea aquí... No creo que esto funcione. Los atributos de JsonProperty en la interfaz se ignoran para el objeto que implementa la interfaz.

contestado el 23 de mayo de 17 a las 14:05

Gracias por el enlace franco, entiendo que los atributos no se heredan de las interfaces a la implementación, ya que sufren el problema de Herencia Múltiple. - Gopal

Esta respuesta está desactualizada, la pregunta vinculada ahora tiene una respuesta que indica que esto se implementó en Json.NET 4.0.3. - dbc

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.