Skip to content

Instantly share code, notes, and snippets.

@PathogenDavid
Last active August 26, 2017 18:40
Show Gist options
  • Save PathogenDavid/434ff4cbb7512576c2b128b74268fb09 to your computer and use it in GitHub Desktop.
Save PathogenDavid/434ff4cbb7512576c2b128b74268fb09 to your computer and use it in GitHub Desktop.
A partially-finished ValueTuple serializer for SharpYaml.
using SharpYaml;
using SharpYaml.Events;
using SharpYaml.Serialization;
using SharpYaml.Serialization.Descriptors;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
namespace YamlPlayground
{
class ValueTupleSerializer : IYamlSerializable, IYamlSerializableFactory
{
public IYamlSerializable TryCreate(SerializerContext context, ITypeDescriptor typeDescriptor)
{
if (typeDescriptor is ObjectDescriptor objectDescriptor && GetFieldCount(objectDescriptor.Type) >= 0)
{ return this; }
return null;
}
/// <summary>
/// Returns the number of fields in the given ValueTuple variant, or -1 if the specified type is not a ValueTuple.
/// </summary>
private static int GetFieldCount(Type tupleType)
{
if (!tupleType.IsGenericType)
{ return -1; }
if (!tupleType.IsGenericTypeDefinition)
{ tupleType = tupleType.GetGenericTypeDefinition(); }
if (tupleType == typeof(ValueTuple))
{ return 0; }
else if (tupleType == typeof(ValueTuple<>))
{ return 1; }
else if (tupleType == typeof(ValueTuple<,>))
{ return 2; }
else if (tupleType == typeof(ValueTuple<,,>))
{ return 3; }
else if (tupleType == typeof(ValueTuple<,,,>))
{ return 4; }
else if (tupleType == typeof(ValueTuple<,,,,>))
{ return 5; }
else if (tupleType == typeof(ValueTuple<,,,,,>))
{ return 6; }
else if (tupleType == typeof(ValueTuple<,,,,,,>))
{ return 7; }
else if (tupleType == typeof(ValueTuple<,,,,,,,>))
{ return 8; }
else
{ return -1; }
}
private static IEnumerable<FieldInfo> GetFieldAccessors(Type tupleType)
{
int fieldCount = GetFieldCount(tupleType);
if (fieldCount < 0)
{ throw new YamlException($"[{tupleType}] is not a valid tuple type."); }
for (int i = 0; i < fieldCount; i++)
{
FieldInfo field = tupleType.GetField(i == 7 ? "Rest" : $"Item{i + 1}");
//TODO: Add support for oversized tuples. (The compiler will make these automatically if you make a tuple over 8 elements long.)
if (GetFieldCount(field.FieldType) >= 0)
{ throw new NotSupportedException("Nested tuples are not yet supported."); }
yield return field;
}
}
public virtual object ReadYaml(ref ObjectContext objectContext)
{
object ret = objectContext.Instance;
var objectDescriptor = (ObjectDescriptor)objectContext.Descriptor;
// Create an empty ValueTuple if we don't have one yet
if (ret == null)
{ ret = FormatterServices.GetUninitializedObject(objectDescriptor.Type); }
// Read in the tuple
objectContext.Reader.Expect<SequenceStart>();
foreach (FieldInfo field in GetFieldAccessors(objectDescriptor.Type))
{
object fieldValue = objectContext.SerializerContext.ReadYaml(null, field.FieldType);
field.SetValue(ret, fieldValue);
}
objectContext.Reader.Expect<SequenceEnd>();
return ret;
}
public virtual void WriteYaml(ref ObjectContext objectContext)
{
object value = objectContext.Instance;
Type valueType = value.GetType();
var objectDescriptor = (ObjectDescriptor)objectContext.Descriptor;
objectContext.Writer.Emit(new SequenceStartEventInfo(value, valueType)
{
Tag = objectContext.Tag,
//TODO: Would it be better to serialize as block only if a tuple contains objects for auto mode?
Style = objectContext.Style == YamlStyle.Any ? YamlStyle.Flow : objectContext.Style
});
foreach (FieldInfo field in GetFieldAccessors(objectDescriptor.Type))
{
object fieldValue = field.GetValue(value);
objectContext.SerializerContext.WriteYaml(fieldValue, field.FieldType);
}
objectContext.Writer.Emit(new SequenceEndEventInfo(value, valueType));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment