如果我定义了一个类
[DataContract()] class MyObject { [DataMember()] ImmutableList<string> Strings { get; private set} }
ImmutableList< T> type来自immutables库https://www.nuget.org/packages/Microsoft.Bcl.Immutable.请注意,类ImmutableList没有默认构造函数或可变Add方法.将内容添加到列表中即可获取表单.
myList = myList.Add("new string");
我是否可以为.NET序列化机制添加一些自定义支持以支持此类型并向其显示如何对其进行反序列化?
目前,在反序列化时只会跳过该集合,尽管可以将其序列化.
解决方法
通过
IDataContractSurrogate接口还有另一种干净的方法. DataContractSerializer允许您为非可序列化对象提供代理.以下是ImmutableList< T>的示例和测试用例.它使用反射,可能可以由比我更聪明的人优化,但在这里.
测试用例
using FluentAssertions; using System.Collections.Immutable; using System.IO; using System.Linq; using System.Runtime.Serialization; using System.Text; using System.Xml; using Xunit; namespace ReactiveUI.Ext.Spec { [DataContract(Name="Node",Namespace="http://foo.com/")] class Node { [DataMember()] public string Name; } [DataContract(Name="Fixture",Namespace="http://foo.com/")] class FixtureType { [DataMember()] public ImmutableList<Node> Nodes; public FixtureType(){ Nodes = ImmutableList<Node>.Empty.AddRange( new [] { new Node(){Name="A"},new Node(){Name="B"},new Node(){Name="C"} }); } } public class ImmutableSurrogateSpec { public static string ToXML(object obj) { var settings = new XmlWriterSettings { Indent = true }; using (MemoryStream memoryStream = new MemoryStream()) using (StreamReader reader = new StreamReader(memoryStream)) using (XmlWriter writer = XmlWriter.Create(memoryStream,settings)) { DataContractSerializer serializer = new DataContractSerializer ( obj.GetType(),new DataContractSerializerSettings() { DataContractSurrogate = new ImmutableSurrogateSerializer() } ); serializer.WriteObject(writer,obj); writer.Flush(); memoryStream.Position = 0; return reader.ReadToEnd(); } } public static T Load<T>(Stream data) { DataContractSerializer ser = new DataContractSerializer ( typeof(T),new DataContractSerializerSettings() { DataContractSurrogate = new ImmutableSurrogateSerializer() } ); return (T)ser.ReadObject(data); } public static T Load<T>(string data) { using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(data))) { return Load<T>(stream); } } [Fact] public void ShouldWork() { var o = new FixtureType(); var s = ToXML(o); var oo = Load<FixtureType>(s); oo.Nodes.Count().Should().Be(3); var names = oo.Nodes.Select(n => n.Name).ToList(); names.ShouldAllBeEquivalentTo(new[]{"A","B","C"}); } } }
实施
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.Serialization; namespace ReactiveUI.Ext { class ImmutableListListConverter<T> { public static ImmutableList<T> ToImmutable( List<T> list ) { return ImmutableList<T>.Empty.AddRange(list); } public static List<T> ToList(ImmutableList<T> list){ return list.ToList(); } public static object ToImmutable( object list ) { return ToImmutable(( List<T> ) list); } public static object ToList(object list){ return ToList(( ImmutableList<T> ) list); } } static class ImmutableListListConverter { static ConcurrentDictionary<Tuple<string,Type>,Func<object,object>> _MethodCache = new ConcurrentDictionary<Tuple<string,object>>(); public static Func<object,object> CreateMethod( string name,Type genericType ) { var key = Tuple.Create(name,genericType); if ( !_MethodCache.ContainsKey(key) ) { _MethodCache[key] = typeof(ImmutableListListConverter<>) .MakeGenericType(new []{genericType}) .GetMethod(name,new []{typeof(object)}) .MakeLambda(); } return _MethodCache[key]; } public static Func<object,object> ToImmutableMethod( Type targetType ) { return ImmutableListListConverter.CreateMethod("ToImmutable",targetType.GenericTypeArguments[0]); } public static Func<object,object> ToListMethod( Type targetType ) { return ImmutableListListConverter.CreateMethod("ToList",targetType.GenericTypeArguments[0]); } private static Func<object,object> MakeLambda(this MethodInfo method ) { return (Func<object,object>) method.CreateDelegate(Expression.GetDelegateType( (from parameter in method.GetParameters() select parameter.ParameterType) .Concat(new[] { method.ReturnType }) .ToArray())); } } public class ImmutableSurrogateSerializer : IDataContractSurrogate { static ConcurrentDictionary<Type,Type> _TypeCache = new ConcurrentDictionary<Type,Type>(); public Type GetDataContractType( Type targetType ) { if ( _TypeCache.ContainsKey(targetType) ) { return _TypeCache[targetType]; } if(targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(ImmutableList<>)) { return _TypeCache[targetType] = typeof(List<>).MakeGenericType(targetType.GetGenericArguments()); } else { return targetType; } } public object GetDeserializedObject( object obj,Type targetType ) { if ( _TypeCache.ContainsKey(targetType) ) { return ImmutableListListConverter.ToImmutableMethod(targetType)(obj); } return obj; } public object GetObjectToSerialize( object obj,Type targetType ) { if ( targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(ImmutableList<>) ) { return ImmutableListListConverter.ToListMethod(targetType)(obj); } return obj; } public object GetCustomDataToExport( Type clrType,Type dataContractType ) { throw new NotImplementedException(); } public object GetCustomDataToExport( System.Reflection.MemberInfo memberInfo,Type dataContractType ) { throw new NotImplementedException(); } public void GetKnownCustomDataTypes( System.Collections.ObjectModel.Collection<Type> customDataTypes ) { throw new NotImplementedException(); } public Type GetReferencedTypeOnImport( string typeName,string typeNamespace,object customData ) { throw new NotImplementedException(); } public System.CodeDom.CodeTypeDeclaration ProcessImportedType( System.CodeDom.CodeTypeDeclaration typeDeclaration,System.CodeDom.CodeCompileUnit compileUnit ) { throw new NotImplementedException(); } public ImmutableSurrogateSerializer() { } } }