Json.net serializar / deserializar tipos derivados?


98

json.net (newtonsoft)
Estoy revisando la documentación pero no puedo encontrar nada sobre esto o la mejor manera de hacerlo.

public class Base
{
    public string Name;
}
public class Derived : Base
{
    public string Something;
}

JsonConvert.Deserialize<List<Base>>(text);

Ahora tengo objetos derivados en la lista serializada. ¿Cómo deserializo la lista y recupero los tipos derivados?


No es así como funciona la herencia. Puede especificar JsonConvert.Deserialize <Derived> (text); para incluir el campo Nombre. Dado que Derived IS A Base (no al revés), Base no sabe nada sobre la definición de Derived.
M.Babcock

Lo siento, aclaro un poco. El problema es que tengo una lista que contiene tanto objetos base como derivados. Así que necesito averiguar cómo le digo a newtonsoft cómo deserializar los elementos derivados.
Será el

Hice que resolviste esto. Tengo el mismo problema
Luis Carlos Chavarría

Respuestas:


46

Si está almacenando el tipo en su text(como debería estar en este escenario), puede usar el JsonSerializerSettings.

Ver: cómo deserializar JSON en IEnumerable <BaseType> con Newtonsoft JSON.NET

Pero ten cuidado. Usar cualquier otra cosa TypeNameHandling = TypeNameHandling.Nonepodría exponerse a una vulnerabilidad de seguridad .


24
También puede usar TypeNameHandling = TypeNameHandling.Auto: esto agregará una $typepropiedad SOLO para instancias donde el tipo declarado (es decir Base) no coincide con el tipo de instancia (es decir Derived). De esta manera, no hincha tanto tu JSON como TypeNameHandling.All.
AJ Richardson

Sigo recibiendo Error al resolver el tipo especificado en JSON '..., ...'. Ruta '$ type', línea 1, posición 82. ¿Alguna idea?
briba

3
Tenga cuidado al usar esto en un punto final público, ya que abre problemas de seguridad: alphabot.com/security/blog/2017/net/…
gjvdkamp

1
@gjvdkamp JEEZ gracias por esto, no sabía nada de esto. Agregará a mi publicación.
kamranicus

96

Debe habilitar el Manejo de nombres de tipo y pasarlo al (des) serializador como parámetro de configuración.

Base object1 = new Base() { Name = "Object1" };
Derived object2 = new Derived() { Something = "Some other thing" };
List<Base> inheritanceList = new List<Base>() { object1, object2 };

JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
string Serialized = JsonConvert.SerializeObject(inheritanceList, settings);
List<Base> deserializedList = JsonConvert.DeserializeObject<List<Base>>(Serialized, settings);

Esto dará como resultado la deserialización correcta de las clases derivadas. Un inconveniente es que nombrará todos los objetos que está usando, como tal, nombrará la lista en la que está colocando los objetos.


31
+1. Estuve buscando en Google durante 30 minutos hasta que descubrí que necesita usar la misma configuración para SerializeObject y DeserializeObject. Supuse que usaría $ type implícitamente si estaba allí al deserializar, tonto.
Erti-Chris Eelmaa

24
TypeNameHandling.Autotambién lo hará, y es más agradable porque no escribe el nombre del tipo de instancia cuando coincide con el tipo de campo / propiedad, que suele ser el caso de la mayoría de los campos / propiedades.
Roman Starkov

2
Esto no funciona cuando la deserialización se realiza en otra solución / proyecto. En la serialización, el nombre de la Solución se incrusta como tipo: "SOLUTIONNAME.Models.Model". En la deserialización en la otra solución, arrojará "JsonSerializationException: no se pudo cargar el ensamblado 'SOLUTIONNAME'.
Triste desarrollador CRUD

19

Dado que la pregunta es tan popular, puede ser útil agregar qué hacer si desea controlar el nombre de la propiedad de tipo y su valor.

El camino más largo es escribir correos electrónicos personalizados JsonConverterpara manejar la (des) serialización comprobando y configurando manualmente la propiedad de tipo.

Una forma más sencilla es usar JsonSubTypes , que maneja todo el texto estándar a través de atributos:

[JsonConverter(typeof(JsonSubtypes), "Sound")]
[JsonSubtypes.KnownSubType(typeof(Dog), "Bark")]
[JsonSubtypes.KnownSubType(typeof(Cat), "Meow")]
public class Animal
{
    public virtual string Sound { get; }
    public string Color { get; set; }
}

public class Dog : Animal
{
    public override string Sound { get; } = "Bark";
    public string Breed { get; set; }
}

public class Cat : Animal
{
    public override string Sound { get; } = "Meow";
    public bool Declawed { get; set; }
}

3
Entiendo la necesidad, pero no soy un fanático de tener que informar a la clase base de todos los "KnownSubType" s ...
Matt Knowles

2
Hay otras opciones si miras la documentación. Solo proporcioné el ejemplo que más me gusta.
rzippo

1
Este es el enfoque más seguro que no expone su servicio a cargar tipos arbitrarios después de la deserialización.
David Burg

3

Use este JsonKnownTypes , es una forma muy similar de usar, solo agrega discriminador a json:

[JsonConverter(typeof(JsonKnownTypeConverter<BaseClass>))]
[JsonKnownType(typeof(Base), "base")]
[JsonKnownType(typeof(Derived), "derived")]
public class Base
{
    public string Name;
}
public class Derived : Base
{
    public string Something;
}

Ahora al serializar objeto en JSON será agrega "$type"con "base"y "derived"valor y se puede utilizar para deserializar

Ejemplo de lista serializada:

[
    {"Name":"some name", "$type":"base"},
    {"Name":"some name", "Something":"something", "$type":"derived"}
]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.