我正在尝试通过以下方式序列化Type对象:
1 2 3 4
| Type myType = typeof (StringBuilder);
var serializer = new XmlSerializer(typeof(Type));
TextWriter writer = new StringWriter();
serializer.Serialize(writer, myType); |
当我这样做时,对Serialize的调用将引发以下异常:
"The type System.Text.StringBuilder was not expected. Use the
XmlInclude or SoapInclude attribute to specify types that are not
known statically."
我有办法序列化Type对象吗? 请注意,我不是在尝试序列化StringBuilder本身,而是在Type对象中包含有关StringBuilder类的元数据。
我不知道只能使用包含完全限定名称的字符串来创建Type对象。要获取标准名称,可以使用以下命令:
1
| string typeName = typeof (StringBuilder).FullName; |
然后,您可以根据需要持久化此字符串,然后像这样重构类型:
1
| Type t = Type.GetType(typeName); |
如果需要创建该类型的实例,则可以执行以下操作:
1
| object o = Activator.CreateInstance(t); |
如果您检查o.GetType()的值,它将是StringBuilder,正如您所期望的那样。
我遇到了同样的问题,我的解决方案是创建一个SerializableType类。它可以自由地与System.Type进行相互转换,但会序列化为字符串。您所要做的就是将变量声明为SerializableType,然后可以将其称为System.Type。
这是课程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| // a version of System.Type that can be serialized
[DataContract]
public class SerializableType
{
public Type type;
// when serializing, store as a string
[DataMember]
string TypeString
{
get
{
if (type == null)
return null;
return type.FullName;
}
set
{
if (value == null)
type = null;
else
{
type = Type.GetType(value);
}
}
}
// constructors
public SerializableType()
{
type = null;
}
public SerializableType(Type t)
{
type = t;
}
// allow SerializableType to implicitly be converted to and from System.Type
static public implicit operator Type(SerializableType stype)
{
return stype.type;
}
static public implicit operator SerializableType(Type t)
{
return new SerializableType(t);
}
// overload the == and != operators
public static bool operator ==(SerializableType a, SerializableType b)
{
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))
{
return false;
}
// Return true if the fields match:
return a.type == b.type;
}
public static bool operator !=(SerializableType a, SerializableType b)
{
return !(a == b);
}
// we don't need to overload operators between SerializableType and System.Type because we already enabled them to implicitly convert
public override int GetHashCode()
{
return type.GetHashCode();
}
// overload the .Equals method
public override bool Equals(System.Object obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// If parameter cannot be cast to SerializableType return false.
SerializableType p = obj as SerializableType;
if ((System.Object)p == null)
{
return false;
}
// Return true if the fields match:
return (type == p.type);
}
public bool Equals(SerializableType p)
{
// If parameter is null return false:
if ((object)p == null)
{
return false;
}
// Return true if the fields match:
return (type == p.type);
}
} |
以及用法示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| [DataContract]
public class A
{
...
[DataMember]
private Dictionary<SerializableType, B> _bees;
...
public B GetB(Type type)
{
return _bees[type];
}
...
} |
您可能还考虑使用AssemblyQualifiedName而不是Type.FullName-请参阅@GreyCloud的评论
如果类型与调用位于同一程序集中,则Brian的答案会很好用(例如其中一条注释中指出的GreyCloud)。
因此,如果类型在另一个程序集中,则需要使用AssemblyQualifiedName,因为GreyCloud也指出了这一点。
但是,当AssemblyQualifiedName保存版本时,如果程序集的版本与您具有该类型的字符串中的版本不同,则它将无法正常工作。
就我而言,这是一个问题,我这样解决了它:
1 2 3 4 5
| string typeName = typeof (MyClass).FullName;
Type type = GetTypeFrom(typeName);
object myInstance = Activator.CreateInstance(type); |
GetTypeFrom方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| private Type GetTypeFrom(string valueType)
{
var type = Type.GetType(valueType);
if (type != null)
return type;
try
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
//To speed things up, we check first in the already loaded assemblies.
foreach (var assembly in assemblies)
{
type = assembly.GetType(valueType);
if (type != null)
break;
}
if (type != null)
return type;
var loadedAssemblies = assemblies.ToList();
foreach (var loadedAssembly in assemblies)
{
foreach (AssemblyName referencedAssemblyName in loadedAssembly.GetReferencedAssemblies())
{
var found = loadedAssemblies.All(x => x.GetName() != referencedAssemblyName);
if (!found)
{
try
{
var referencedAssembly = Assembly.Load(referencedAssemblyName);
type = referencedAssembly.GetType(valueType);
if (type != null)
break;
loadedAssemblies.Add(referencedAssembly);
}
catch
{
//We will ignore this, because the Type might still be in one of the other Assemblies.
}
}
}
}
}
catch(Exception exception)
{
//throw my custom exception
}
if (type == null)
{
//throw my custom exception.
}
return type;
} |
如果有人需要,我会发布此信息。
根据System.Type [1]的MSDN文档,您应该能够序列化System.Type对象。但是,由于该错误明确地指向System.Text.StringBuilder,因此很可能是导致序列化错误的类。
[1]类型类(系统)-http://msdn.microsoft.com/zh-cn/library/system.type.aspx
仅查看其定义,它就没有标记为Serializable。如果您确实需要将此数据进行序列化,则可能必须将其转换为标记为此类的自定义类。
1 2 3 4 5 6 7 8 9 10
| public abstract class Type : System.Reflection.MemberInfo
Member of System
Summary:
Represents type declarations: class types, interface types, array types, value types, enumeration types, type parameters, generic type definitions, and open or closed constructed generic types.
Attributes:
[System.Runtime.InteropServices.ClassInterfaceAttribute(0),
System.Runtime.InteropServices.ComDefaultInterfaceAttribute(System.Runtime.InteropServices._Type),
System.Runtime.InteropServices.ComVisibleAttribute(true)] |
我在尝试在.net标准2.0中进行二进制序列化时遇到了这个问题。我最终使用自定义的SurrogateSelector和SerializationBinder解决了问题。
需要TypeSerializationBinder是因为框架在获取SurrogateSelector之前无法解决System.RuntimeType。我真的不明白为什么在此步骤之前必须先解析类型...
这是代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| // Serializes and deserializes System.Type
public class TypeSerializationSurrogate : ISerializationSurrogate {
public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) {
info.AddValue(nameof(Type.FullName), (obj as Type).FullName);
}
public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) {
return Type.GetType(info.GetString(nameof(Type.FullName)));
}
}
// Just a stub, doesn't need an implementation
public class TypeStub : Type { ... }
// Binds"System.RuntimeType" to our TypeStub
public class TypeSerializationBinder : SerializationBinder {
public override Type BindToType(string assemblyName, string typeName) {
if(typeName =="System.RuntimeType") {
return typeof(TypeStub);
}
return Type.GetType($"{typeName}, {assemblyName}");
}
}
// Selected out TypeSerializationSurrogate when [de]serializing Type
public class TypeSurrogateSelector : ISurrogateSelector {
public virtual void ChainSelector(ISurrogateSelector selector) => throw new NotSupportedException();
public virtual ISurrogateSelector GetNextSelector() => throw new NotSupportedException();
public virtual ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector) {
if(typeof(Type).IsAssignableFrom(type)) {
selector = this;
return new TypeSerializationSurrogate();
}
selector = null;
return null;
}
} |
用法示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| byte[] bytes
var serializeFormatter = new BinaryFormatter() {
SurrogateSelector = new TypeSurrogateSelector()
}
using (var stream = new MemoryStream()) {
serializeFormatter.Serialize(stream, typeof(string));
bytes = stream.ToArray();
}
var deserializeFormatter = new BinaryFormatter() {
SurrogateSelector = new TypeSurrogateSelector(),
Binder = new TypeDeserializationBinder()
}
using (var stream = new MemoryStream(bytes)) {
type = (Type)deserializeFormatter .Deserialize(stream);
Assert.Equal(typeof(string), type);
} |