Last active
August 26, 2017 18:40
-
-
Save PathogenDavid/434ff4cbb7512576c2b128b74268fb09 to your computer and use it in GitHub Desktop.
A partially-finished ValueTuple serializer for SharpYaml.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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