More complete IFormatter implementation reference for students preparing for MS 70-483
public class IniFormatter : IFormatter | |
{ | |
public class IniTypeBinder : SerializationBinder | |
{ | |
public override Type BindToType(string assemblyName, string typeName) => Type.GetType(typeName.Split('=')[1]); | |
public override void BindToName(Type serializedType, out string assemblyName, out string typeName) | |
{ | |
assemblyName = $"{IniFormatter.AssemblyNameKey}={serializedType.Assembly.FullName}"; | |
typeName = $"{IniFormatter.ClassNameKey}={serializedType.AssemblyQualifiedName}"; | |
} | |
} | |
public ISurrogateSelector SurrogateSelector { get; set; } | |
public StreamingContext Context { get; set; } | |
private IniTypeBinder serializationBinder; | |
public SerializationBinder Binder | |
{ | |
get => serializationBinder; | |
set | |
{ | |
if (!(value is IniTypeBinder iniTypeBinder)) | |
throw new NotSupportedException("Sorry, only IniTypeBinder implementations are supported"); | |
serializationBinder = (IniTypeBinder)value; | |
} | |
} | |
private const string ClassNameKey = "@Class"; | |
private const string AssemblyNameKey = "@Assembly"; | |
public IniFormatter() | |
{ | |
Context = new StreamingContext(StreamingContextStates.All); | |
Binder = new IniTypeBinder(); | |
} | |
#region serialization | |
public void Serialize(Stream serializationStream, object graph) | |
{ | |
var objectType = graph.GetType(); | |
var serializationSurrogate = SurrogateSelector?.GetSurrogate(objectType, Context, out var _); | |
if (serializationSurrogate != null) | |
SerializeWithSurrogate(serializationStream, graph, objectType, serializationSurrogate); | |
else if (graph is ISerializable serializable) | |
SerializeAsISerializable(serializationStream, graph, objectType, serializable); | |
else | |
SerializeWithFormatterServices(serializationStream, graph, objectType); | |
GetCallbackDelegate(objectType, typeof(OnSerializedAttribute))?.DynamicInvoke(graph, Context); | |
} | |
private void SerializeWithFormatterServices(Stream serializationStream, object graph, Type objectType) | |
{ | |
if (!objectType.IsSerializable) | |
throw new SerializationException($"Type {objectType} is not serializable"); | |
var members = FormatterServices.GetSerializableMembers(objectType, this.Context); | |
var memberData = FormatterServices.GetObjectData(graph, members); | |
GetCallbackDelegate(objectType, typeof(OnSerializingAttribute))?.DynamicInvoke(graph, Context); | |
using (var sw = new StreamWriter(serializationStream)) | |
{ | |
WriteTypeName(objectType, sw); | |
foreach (var m in members) | |
{ | |
sw.WriteLine($"{m.Name}={m.ToString()}"); | |
} | |
} | |
} | |
private void WriteTypeName(Type objectType, StreamWriter sw) | |
{ | |
Binder.BindToName(objectType, out var assemblyName, out var typeName); | |
sw.WriteLine(typeName); | |
sw.WriteLine(assemblyName); | |
} | |
private void SerializeAsISerializable(Stream serializationStream, object graph, Type objectType, ISerializable serializable) | |
{ | |
if (!objectType.IsSerializable) | |
throw new SerializationException($"Type {objectType} is not serializable"); | |
var serializationInfo = new SerializationInfo(objectType, new FormatterConverter()); | |
serializable.GetObjectData(serializationInfo, Context); | |
SerializeFromSerializationInfo(serializationStream, graph, serializationInfo); | |
} | |
private void SerializeWithSurrogate(Stream serializationStream, object graph, Type objectType, ISerializationSurrogate serializationSurrogate) | |
{ | |
var serializationInfo = new SerializationInfo(objectType, new FormatterConverter()); | |
serializationSurrogate.GetObjectData(graph, serializationInfo, Context); | |
SerializeFromSerializationInfo(serializationStream, graph, serializationInfo); | |
} | |
private Delegate GetCallbackDelegate(Type objectType, Type methodAttribute) | |
{ | |
var onSerializingMethod = objectType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) | |
.SingleOrDefault(m => m.GetCustomAttribute<OnSerializingAttribute>() != null); | |
if (onSerializingMethod == null) | |
return null; | |
if (!(onSerializingMethod.ReturnType == typeof(void) && | |
onSerializingMethod.GetParameters().Length == 1 && | |
onSerializingMethod.GetParameters()[0].ParameterType == typeof(StreamingContext)) && | |
!onSerializingMethod.IsVirtual) | |
throw new InvalidOperationException($"Method {onSerializingMethod.Name} found with {methodAttribute}, but method is not compliant with the requirements of this attribute"); | |
var funcType = typeof(Action<,>).MakeGenericType(objectType, typeof(StreamingContext)); | |
return Delegate.CreateDelegate(funcType, onSerializingMethod); | |
} | |
private void SerializeFromSerializationInfo(Stream serializationStream, object graph, SerializationInfo serializationInfo) | |
{ | |
GetCallbackDelegate(graph.GetType(), typeof(OnSerializingAttribute))?.DynamicInvoke(graph, Context); | |
using (var sw = new StreamWriter(serializationStream)) | |
{ | |
WriteTypeName(graph.GetType(), sw); | |
foreach (var item in serializationInfo) | |
{ | |
sw.WriteLine($"{item.Name}={item.Value.ToString()}"); | |
} | |
} | |
} | |
#endregion | |
#region deserialization | |
public object Deserialize(Stream serializationStream) | |
{ | |
using (var sr = new StreamReader(serializationStream)) | |
{ | |
var typeInfo = sr.ReadLine(); | |
var assemblyInfo = sr.ReadLine(); | |
Type objectType = Binder.BindToType(assemblyInfo, typeInfo); | |
var serializationSurrogate = SurrogateSelector?.GetSurrogate(objectType, Context, out var _); | |
if (serializationSurrogate != null) | |
return DeserializeWithSurrogate(sr, objectType, serializationSurrogate); | |
else if (typeof(ISerializable).IsAssignableFrom(objectType)) | |
return DeserializeAsISerializable(sr, objectType); | |
else | |
return DeserializeWithFormatterServices(sr, objectType); | |
} | |
} | |
private object DeserializeAsISerializable(StreamReader sr, Type objectType) | |
{ | |
throw new NotImplementedException(); | |
} | |
private object DeserializeWithSurrogate(StreamReader sr, Type objectType, ISerializationSurrogate serializationSurrogate) | |
{ | |
throw new NotImplementedException(); | |
} | |
private object DeserializeWithFormatterServices(StreamReader serializationReader, Type objectType) | |
{ | |
var result = FormatterServices.GetUninitializedObject(objectType); | |
var members = FormatterServices.GetSerializableMembers(objectType, this.Context); | |
var serializationData = new Dictionary<string, object>(); | |
while (!serializationReader.EndOfStream) | |
{ | |
var data = serializationReader.ReadLine(); | |
var splitData = data.Split('='); | |
serializationData.Add(splitData[0], splitData[1]); | |
} | |
var correctedTypes = new List<object>(members.Length); | |
for (int i = 0; i < members.Length; i++) | |
{ | |
var f = (FieldInfo)members[i]; | |
correctedTypes.Add(Convert.ChangeType(serializationData[f.Name], f.FieldType)); | |
} | |
FormatterServices.PopulateObjectMembers(result, members, correctedTypes.ToArray()); | |
return result; | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment