Pregunta

Estoy intentando serializar un objeto Type de la siguiente manera:

Type myType = typeof (StringBuilder);
var serializer = new XmlSerializer(typeof(Type));
TextWriter writer = new StringWriter();
serializer.Serialize(writer, myType);

Cuando hago esto, la llamada a Serialize arroja la siguiente excepción:

"No se esperaba el tipo System.Text.StringBuilder.Use el atributo XmlinClude o Soapinclude para especificar tipos que no se conocen estáticamente ".

¿Hay alguna manera de serializar el Type ¿objeto?Tenga en cuenta que no estoy intentando serializar el StringBuilder en sí, pero el Type objeto que contiene los metadatos sobre el StringBuilder clase.

¿Fue útil?

Solución

No sabía que se podía crear un objeto Tipo con solo una cadena que contenga el nombre completo.Para obtener el nombre completo, puede utilizar lo siguiente:

string typeName = typeof (StringBuilder).FullName;

Luego puede conservar esta cadena según sea necesario y luego reconstruir el tipo de esta manera:

Type t = Type.GetType(typeName);

Si necesita crear una instancia del tipo, puede hacer esto:

object o = Activator.CreateInstance(t);

Si marca el valor de o.GetType(), será StringBuilder, tal como era de esperar.

Otros consejos

Tuve el mismo problema y mi solución fue crear una clase SerializableType.Se convierte libremente hacia y desde System.Type, pero se serializa como una cadena.Todo lo que tienes que hacer es declarar la variable como SerializableType y, a partir de ese momento, podrás referirte a ella como System.Type.

Aquí está la clase:

// 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);
    }
}

y un ejemplo de uso:

[DataContract]
public class A
{

    ...

    [DataMember]
    private Dictionary<SerializableType, B> _bees;

    ...

    public B GetB(Type type)
    {
        return _bees[type];
    }

    ...

}

También podría considerar usar AssemblyQualifiedName en lugar de Type.FullName; consulte el comentario de @GreyCloud

Brian's La respuesta funciona bien si el tipo está en el mismo ensamblaje que la llamada (como señaló GreyCloud en uno de los comentarios).Entonces, si el tipo está en otro ensamblaje, debe usar el Nombre calificado de ensamblaje como también señaló GreyCloud.

Sin embargo como el Nombre calificado de ensamblaje guarda la versión, si sus ensamblados tienen una versión diferente a la de la cadena donde tiene el tipo, no funcionará.

En mi caso esto fue un problema y lo resolví así:

string typeName = typeof (MyClass).FullName;

Type type = GetTypeFrom(typeName);

object myInstance = Activator.CreateInstance(type);

Método GetTypeFrom

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;
    }

Publico esto por si alguien lo necesita.

Según la documentación de MSDN de System.Type [1], debería poder serializar el objeto System.Type.Sin embargo, como el error se refiere explícitamente a System.Text.StringBuilder, es probable que esa sea la clase que está causando el error de serialización.

[1] Clase de tipo (Sistema) - http://msdn.microsoft.com/en-us/library/system.type.aspx

Solo miré su definición, no está marcado como Serializable.Si realmente necesita serializar estos datos, es posible que deba convertirlos a una clase personalizada marcada como tal.

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)]

Me encontré con este problema al intentar realizar una serialización binaria en .net estándar 2.0.Terminé resolviendo el problema usando un personalizado SurrogateSelector y SerializationBinder.

El TypeSerializationBinder era necesario porque el marco tenía problemas para resolver System.RuntimeType antes de que llegara SurrogateSelector.Sin embargo, realmente no entiendo por qué el tipo debe resolverse antes de este paso...

Aquí está el código:

// 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;
    }
}

Ejemplo de uso:

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);
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top