Usando YamlDotNet , intento deserializar el siguiente YAML:
Collection: - Type: TypeA TypeAProperty: value1 - Type: TypeB TypeBProperty: value2
La propiedad Type
es una propiedad necesaria para todos los objetos de Collection
. El resto de las propiedades dependen del tipo.
Este es mi modelo de objeto ideal:
public class Document { public IEnumerable<IBaseObject> Collection { get; set; } } public interface IBaseObject { public string Type { get; } } public class TypeAClass : IBaseObject { public string Type { get; set; } public string TypeAProperty { get; set; } } public class TypeBClass : IBaseObject { public string Type { get; set; } public string TypeBProperty { get; set; } }
Según mi lectura, creo que mi mejor apuesta es usar un deserializador de nodo personalizado, derivado de INodeDeserializer
. Como prueba de concepto, puedo hacer esto:
public class MyDeserializer : INodeDeserializer { public bool Deserialize(IParser parser, Type expectedType, Func<IParser, Type, object> nestedObjectDeserializer, out object value) { if (expectedType == typeof(IBaseObject)) { Type type = typeof(TypeAClass); value = nestedObjectDeserializer(parser, type); return true; } value = null; return false; } }
Mi problema ahora es cómo determinar dinámicamente el Type
a elegir antes de llamar a nestedObjectDeserializer
.
Al usar JSON.Net, pude usar un CustomCreationConverter
, leer el sub-JSON en un JObject
, determinar mi tipo, luego crear un nuevo JsonReader
desde el JObject
y volver a analizar el objeto.
¿Hay alguna manera de que pueda leer, retroceder y luego volver a leer nestedObjectDeserializer
?
¿Hay otro tipo de objeto al que pueda llamar en nestedObjectDeserializer
, luego, a partir de eso, lea la propiedad Type
, finalmente continúe con el análisis normal de YamlDotNet del tipo derivado?
No es fácil. Aquí hay un problema de GitHub que explica cómo hacer una serialización polimórfica usando YamlDotNet.
Una solución simple en su caso es la deserialización en dos pasos. Primero, deserializa en algún formulario intermediario y luego lo convierte a sus modelos. Eso es relativamente fácil ya que limita la excavación en el interior de YamlDotNet:
public class Step1Document { public List<Step1Element> Collection { get; set; } public Document Upcast() { return new Document { Collection = Collection.Select(m => m.Upcast()).ToList() }; } } public class Step1Element { // Fields from TypeA and TypeB public string Type { get; set; } public string TypeAProperty { get; set; } public string TypeBProperty { get; set; } internal IBaseObject Upcast() { if(Type == "TypeA") { return new TypeAClass { Type = Type, TypeAProperty = TypeAProperty }; } if (Type == "TypeB") { return new TypeBClass { Type = Type, TypeBProperty = TypeBProperty }; } throw new NotImplementedException(Type); } }
Y eso para deserializar:
var serializer = new DeserializerBuilder().Build(); var document = serializer.Deserialize<Step1Document>(data).Upcast();