Jiten Jiten - 16 days ago 13
YAML Question

How to Serialize and Deserialize a Type class object in C# into YAML?

I have a requirement where I have to serialize the following type hierarchy into YAML

var Variable1 = new
{
Name = "Variable1",
Type = typeof(Int32),
OverWrite = true,
Value = 10
};
var Variable2 = new
{
Name = "Variable1",
Type = typeof(Int32),
OverWrite = true,
Value = 10
};

var Job = new
{
Name = "Job1",
JobID = 1,
JobState = "Draft",
JobStatus = false,
Parameters = new[]
{
Variable1,
Variable2
},
LocalVariables = new[]
{
Variable1
}
};


Here I am getting an exception as

An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll


Additional information: Exception has been thrown by the target of an invocation.{"Method may only be called on a Type for which Type.IsGenericParameter is true."}

Please help !!

Answer

This is because you are trying to serialize a System.Type. That type has lots of properties and some of them throw the exception that you are seeing. This was discussed on issue #212, although in that case the fix was to completely avoid serializing the System.Type.

Ideally, you would be able to register a custom type converter to handle System.Type and serialize it as a string, but due to a shortcoming with the way the object graph is traversed, the property will still be accessed.

Your best solution is probably to wrap the System.Type inside a custom class that serializes as you want:

public struct SerializableType : IYamlConvertible
{
    private Type type;

    void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer)
    {
        var typeName = (string)nestedObjectDeserializer(typeof(string));
        type = typeName != null ? Type.GetType(typeName) : null;
    }

    void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer)
    {
        nestedObjectSerializer(type != null ? type.AssemblyQualifiedName : null);
    }

    public static implicit operator Type(SerializableType value)
    {
        return value.type;
    }

    public static implicit operator SerializableType(Type value)
    {
        return new SerializableType { type = value };
    }
}

Edit

The issue mentioned has been fixed. If you try the latest pre-release package, you will be able to achieve what you want by registering a custom IYamlTypeConverter:

public class SystemTypeTypeConverter : IYamlTypeConverter
{
    public bool Accepts(Type type)
    {
        return typeof(Type).IsAssignableFrom(type);
    }

    public object ReadYaml(IParser parser, Type type)
    {
        var scalar = parser.Expect<Scalar>();
        return Type.GetType(scalar.Value);
    }

    public void WriteYaml(IEmitter emitter, object value, Type type)
    {
        var typeName = ((Type)value).AssemblyQualifiedName;
        emitter.Emit(new Scalar(typeName));
    }
}

// ....

var serializer = new SerializerBuilder()
    .WithTypeConverter(new SystemTypeTypeConverter())
    .Build();

var yaml = serializer.Serialize(new TypeContainer
{
    Type = typeof(string),
});

var deserializer = new DeserializerBuilder()
    .WithTypeConverter(new SystemTypeTypeConverter())
    .Build();

var result = deserializer.Deserialize<TypeContainer>(yaml);

Assert.Equal(typeof(string), result.Type);