49 votos

El uso de Json.NET los convertidores para deserializar propiedades

Tengo una definición de clase que contiene una propiedad que devuelve una interfaz.

public class Foo
{ 
    public int Number { get; set; }

    public ISomething Thing { get; set; }
}

Tratando de serializar la clase Foo utilizando Json.NET me da un mensaje de error como "no se Pudo crear una instancia de tipo 'ISomething'. ISomething puede ser una interfaz o clase abstracta."

Hay un Json.NET atributo o convertidor que me permiten especificar un hormigón Something de la clase a utilizar durante la deserialización?

60voto

Daniel T. Puntos 7990

Una de las cosas que usted puede hacer con Json.NET es:

var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;

JsonConvert.SerializeObject(entity, Formatting.Indented, settings);

El TypeNameHandling bandera de agregar un $type de propiedad para el JSON, lo que permite Json.NET para saber que tipo concreto necesita deserializar el objeto en. Esto permite que usted para deserializar un objeto al mismo tiempo el cumplimiento de una interfaz o clase base abstracta.

La desventaja, sin embargo, es que esto es muy Json.NET-específicos. El $type será un completo tipo, así que si usted está serializando con el tipo de info,, el deserializer debe ser capaz de entenderlo así.

Documentación: la Serialización de Configuración con Json.NET

32voto

MrMDavidson Puntos 1591

Esto se puede lograr mediante el uso de la JsonConverter clase. Supongamos que tenemos una clase con una interfaz de propiedad;

public class Organisation {
  public string Name { get; set; }

  [JsonConverter(typeof(TycoonConverter))]
  public IPerson Owner { get; set; }
}

public interface IPerson {
  string Name { get; set; }
}

public class Tycoon : IPerson {
  public string Name { get; set; }
}

Su JsonConverter es responsable de registrar y de registrar la propiedad subyacente;

public class TycoonConverter : JsonConverter
{
  public override bool CanConvert(Type objectType)
  {
    return (objectType == typeof(IPerson));
  }

  public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  {
    return serializer.Deserialize<Tycoon>(reader);
  }

  public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  {
    // Left as an exercise to the reader :)
    throw new NotImplementedException();
  }
}

Cuando se trabaja con una Organización deserializa a través de Json.Net el subyacente IPerson para el Dueño de la propiedad será de tipo Tycoon.

21voto

Erhhung Puntos 199

En lugar de pasar de una manera personalizada, JsonSerializerSettings objeto JsonConvert.SerializeObject() con el TypeNameHandling.Opción de objetos, como se mencionó anteriormente, usted puede simplemente marca que la interfaz específica de la propiedad con un atributo para generar el JSON no ser hinchado con "$" tipo de propiedades en CADA objeto:

public class Foo
{
    public int Number { get; set; }

    // Add "$type" property containing type info of concrete class.
    [JsonProperty( TypeNameHandling = TypeNameHandling.Objects )]
    public ISomething { get; set; }
}

11voto

SamuelDavis Puntos 830

En la más reciente versión de la tercera parte Newtonsoft Json converter puede establecer un constructor con un tipo concreto relativos a la interfaz de la propiedad.

public class Foo
{ 
    public int Number { get; private set; }

    public ISomething IsSomething { get; private set; }

    public Foo(int number, Something concreteType)
    {
        Number = number;
        IsSomething = concreteType;
    }
}

Mientras Algo se implementa ISomething esto debería funcionar. También no poner un defecto constructor vacío en caso de que el JSon converter intenta utilizar que, debe forzar a utilizar el constructor que contiene el tipo concreto.

PS. esto también permite que usted para hacer de su incubadoras privadas.

9voto

Bruno Altinet Puntos 116

Había un mismo problema así que se me ocurrió con mi propio Convertidor que utiliza conocido los tipos de argumento.

public class JsonKnownTypeConverter : JsonConverter
{
public IEnumerable<Type> KnownTypes { get; set; }
public JsonKnownTypeConverter(IEnumerable<Type> knownTypes)
{
    KnownTypes = knownTypes;
}

protected object Create(Type objectType, JObject jObject)
{
    if (jObject["$type"] != null)
    {
        string typeName = jObject["$type"].ToString();
        return Activator.CreateInstance(KnownTypes.First(x =>typeName.Contains("."+x.Name+",")));
    }
    throw new InvalidOperationException("No supported type");
}

public override bool CanConvert(Type objectType)
{
    if (KnownTypes == null)
        return false;
    return (objectType.IsInterface || objectType.IsAbstract) && KnownTypes.Any(objectType.IsAssignableFrom);
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    // Load JObject from stream
    JObject jObject = JObject.Load(reader);
    // Create target object based on JObject
    var target = Create(objectType, jObject);
    // Populate the object properties
    serializer.Populate(jObject.CreateReader(), target);
    return target;
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    throw new NotImplementedException();
}
}

He definido dos métodos de extensión para deserializar y registrar:

public static class AltiJsonSerializer
{
public static T DeserializeJson<T>(this string jsonString, IEnumerable<Type> knownTypes = null)
{
    if (string.IsNullOrEmpty(jsonString))
        return default(T);
    return JsonConvert.DeserializeObject<T>(jsonString,
                                            new JsonSerializerSettings
                                                {
                                                    TypeNameHandling = TypeNameHandling.Auto,
                                                    Converters = new List<JsonConverter>(
                                                        new JsonConverter[]
                                                        {
                                                           new JsonKnownTypeConverter(knownTypes)
                                                        })

                                                });
}

public static string SerializeJson(this object objectToSerialize)
{
    return JsonConvert.SerializeObject(objectToSerialize, Formatting.Indented,
                                       new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto});
}
}

Usted puede definir su propio modo de comparación y la identificación de tipos en la convertes, yo sólo uso el nombre de la clase.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by: