Estoy tratando de extender el ejemplo de JSON.net que se proporciona aquí http://james.newtonking.com/projects/json/help/CustomCreationConverter.html
Tengo otra subclase derivada de la clase base / interfaz
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Employee : Person
{
public string Department { get; set; }
public string JobTitle { get; set; }
}
public class Artist : Person
{
public string Skill { get; set; }
}
List<Person> people = new List<Person>
{
new Employee(),
new Employee(),
new Artist(),
};
¿Cómo deserializar siguiendo a Json de regreso a la Lista <Persona>
[
{
"Department": "Department1",
"JobTitle": "JobTitle1",
"FirstName": "FirstName1",
"LastName": "LastName1"
},
{
"Department": "Department2",
"JobTitle": "JobTitle2",
"FirstName": "FirstName2",
"LastName": "LastName2"
},
{
"Skill": "Painter",
"FirstName": "FirstName3",
"LastName": "LastName3"
}
]
No quiero usar TypeNameHandling JsonSerializerSettings. Estoy buscando específicamente la implementación personalizada de JsonConverter para manejar esto. La documentación y los ejemplos sobre esto son bastante escasos en la red. Parece que no puedo obtener la implementación anulada del método ReadJson () en JsonConverter correctamente.
c#
json
json.net
deserialization
Snakebyte
fuente
fuente
Respuestas:
Usando el estándar
CustomCreationConverter
, estaba luchando para trabajar cómo generar el tipo correcto (Person
oEmployee
), porque para determinar esto necesitas analizar el JSON y no hay una forma integrada de hacerlo usando elCreate
método.Encontré un hilo de discusión relacionado con la conversión de tipos y resultó proporcionar la respuesta. Aquí hay un enlace: Tipo de conversión .
Lo que se requiere es subclasificar
JsonConverter
, anular elReadJson
método y crear un nuevoCreate
método abstracto que acepte aJObject
.El
ReadJson
método reemplazado creaJObject
ay e invoca elCreate
método (implementado por nuestra clase de convertidor derivado), pasando laJObject
instancia.Esta
JObject
instancia se puede analizar para determinar el tipo correcto comprobando la existencia de ciertos campos.Ejemplo
fuente
JsonReader
creado en elReadJson
método no hereda cualquiera de los valores de configuración del lector original, (Culture
,DateParseHandling
,DateTimeZoneHandling
,FloatParseHandling
, etc ...). Estos valores deben copiarse antes de usar el nuevoJsonReader
enserializer.Populate()
.JsonConverter
tiene una propiedad llamadaCanRead
yCanWrite
. Si no necesita unaWriteJson
implementación personalizada , es suficiente para queCanWrite
regreseFALSE
. El sistema volverá al comportamiento predeterminado. @jdavies: agregue eso a su respuesta. De lo contrario, se bloqueará en la serialización.La solución anterior para
JsonCreationConverter<T>
todo está en Internet, pero tiene una falla que se manifiesta en raras ocasiones. El nuevo JsonReader creado en el método ReadJson no hereda ninguno de los valores de configuración del lector original (Culture, DateParseHandling, DateTimeZoneHandling, FloatParseHandling, etc.). Estos valores deben copiarse antes de usar el nuevo JsonReader en serializer.Populate ().Esto es lo mejor que se me ocurrió para solucionar algunos de los problemas con la implementación anterior, pero sigo pensando que hay algunas cosas que se pasan por alto:
Actualización Actualicé esto para tener un método más explícito que haga una copia de un lector existente. Esto simplemente encapsula el proceso de copia sobre configuraciones individuales de JsonReader. Idealmente, esta función se mantendría en la propia biblioteca de Newtonsoft, pero por ahora, puede usar lo siguiente:
Esto debe usarse de la siguiente manera:
La solución más antigua sigue:
fuente
Solo pensé que compartiría una solución también basada en esto que funciona con el atributo Knowntype usando la reflexión, tenía que obtener una clase derivada de cualquier clase base, la solución puede beneficiarse de la recursividad para encontrar la mejor clase de coincidencia, aunque no la necesitaba en mi caso, la coincidencia se realiza por el tipo dado al convertidor si tiene KnownTypes, los escaneará a todos hasta que coincida con un tipo que tenga todas las propiedades dentro de la cadena json, se elegirá el primero que coincida.
el uso es tan simple como:
en el caso anterior ret será de tipo B.
Clases JSON:
Código convertidor:
fuente
El proyecto JsonSubTypes implementa un convertidor genérico que maneja esta característica con la ayuda de los atributos.
Para la muestra de concreto proporcionada aquí es cómo funciona:
fuente
Esta es una expansión de la respuesta del tótem. Básicamente hace lo mismo, pero la coincidencia de propiedades se basa en el objeto json serializado, no refleja el objeto .net. Esto es importante si está utilizando [JsonProperty], utilizando CamelCasePropertyNamesContractResolver o haciendo cualquier otra cosa que haga que json no coincida con el objeto .net.
El uso es simple:
Código convertidor:
fuente
Como otra variación de la solución de tipo conocida de Totem, puede usar la reflexión para crear un solucionador de tipo genérico para evitar la necesidad de usar atributos de tipo conocidos.
Esto utiliza una técnica similar a la resolución genérica de Juval Lowy para WCF.
Mientras su clase base sea abstracta o una interfaz, los tipos conocidos se determinarán automáticamente en lugar de tener que ser decorados con atributos de tipo conocidos.
En mi propio caso, opté por usar una propiedad $ type para designar el tipo en mi objeto json en lugar de tratar de determinarlo a partir de las propiedades, aunque podría tomar prestado de otras soluciones aquí para usar la determinación basada en la propiedad.
Luego se puede instalar como formateador
fuente
Aquí hay otra solución que evita el uso de
jObject.CreateReader()
, y en su lugar crea una nuevaJsonTextReader
(que es el comportamiento utilizado por elJsonCreate.Deserialze
método predeterminado :fuente
Muchas veces la implementación existirá en el mismo espacio de nombres que la interfaz. Entonces, se me ocurrió esto:
Por lo tanto, puede incluir esto globalmente de la siguiente manera:
fuente
Usando la idea de totem y zlangner , he creado un
KnownTypeConverter
que será capaz de determinar el heredero más apropiado, teniendo en cuenta que los datos json pueden no tener elementos opcionales.Entonces, el servicio envía una respuesta JSON que contiene una variedad de documentos (entrantes y salientes). Los documentos tienen un conjunto común de elementos y diferentes. En este caso, los elementos relacionados con los documentos salientes son opcionales y pueden estar ausentes.
En este sentido,
Document
se creó una clase base que incluye un conjunto común de propiedades. También se crean dos clases de herederos: -OutgoingDocument
agrega dos elementos opcionales"device_id"
y"msg_id"
; -IncomingDocument
agrega un elemento obligatorio"sender_id"
;La tarea consistía en crear un convertidor que, basado en datos e información json de KnownTypeAttribute, pudiera determinar la clase más apropiada que le permita guardar la mayor cantidad de información recibida. También se debe tener en cuenta que los datos json pueden no tener elementos opcionales. Para reducir el número de comparaciones de elementos json y propiedades de modelos de datos, decidí no tener en cuenta las propiedades de la clase base y correlacionar con elementos json solo las propiedades de las clases herederas.
Datos del servicio:
Modelos de datos:
Convertidor:
PD: en mi caso, si el convertidor no ha seleccionado a ningún heredero (esto puede suceder si los datos JSON contienen información solo de la clase base o los datos JSON no contienen elementos opcionales de
OutgoingDocument
), entonces un objeto de laOutgoingDocument
clase se creará, ya que aparece en primer lugar en la lista deKnownTypeAttribute
atributos. A petición suya, puede variar la implementación de laKnownTypeConverter
en esta situación.fuente