Domanda

Sto cercando di serializzare un oggetto Type nel modo seguente:

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

Quando lo faccio, la chiamata a Serialize genera la seguente eccezione:

"Il tipo System.Text.StringBuilder non era previsto.Utilizzare l'attributo xmlinclude o soapinclude per specificare tipi che non sono noti staticamente. "

C'è un modo per serializzare il file Type oggetto?Tieni presente che non sto cercando di serializzare il file StringBuilder stesso, ma il Type oggetto contenente i metadati relativi a StringBuilder classe.

È stato utile?

Soluzione

Non ero a conoscenza del fatto che un oggetto Type potesse essere creato solo con una stringa contenente il nome completo.Per ottenere il nome completo, è possibile utilizzare quanto segue:

string typeName = typeof (StringBuilder).FullName;

Puoi quindi persistere questa stringa secondo necessità, quindi ricostruire il tipo in questo modo:

Type t = Type.GetType(typeName);

Se devi creare un'istanza del tipo, puoi farlo:

object o = Activator.CreateInstance(t);

Se controlli il valore di o.GetType(), sarà StringBuilder, proprio come ti aspetteresti.

Altri suggerimenti

Ho avuto lo stesso problema e la mia soluzione era creare una classe SerializableType.Viene convertito liberamente da e verso System.Type, ma viene serializzato come stringa.Tutto quello che devi fare è dichiarare la variabile come SerializableType e da quel momento in poi potrai fare riferimento ad essa come System.Type.

Ecco la classe:

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

e un esempio di utilizzo:

[DataContract]
public class A
{

    ...

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

    ...

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

    ...

}

Potresti anche prendere in considerazione l'utilizzo di AssemblyQualifiedName invece di Type.FullName - vedi il commento di @GreyCloud

Quello di Brian la risposta funziona bene se il tipo è nello stesso assembly della chiamata (come sottolineato da GreyCloud in uno dei commenti).Quindi, se il tipo si trova in un altro assembly, è necessario utilizzare il file NomeQualificatoAssemblea come ha sottolineato anche GreyCloud.

Tuttavia come il NomeQualificatoAssemblea salva la versione, se i tuoi assembly hanno una versione diversa da quella nella stringa in cui hai il tipo, non funzionerà.

Nel mio caso questo era un problema e l'ho risolto in questo modo:

string typeName = typeof (MyClass).FullName;

Type type = GetTypeFrom(typeName);

object myInstance = Activator.CreateInstance(type);

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

Lo pubblico nel caso in cui qualcuno ne abbia bisogno.

Secondo la documentazione MSDN di System.Type [1] dovresti essere in grado di serializzare l'oggetto System.Type.Tuttavia, poiché l'errore si riferisce esplicitamente a System.Text.StringBuilder, è probabile che sia questa la classe che causa l'errore di serializzazione.

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

Ho appena guardato la sua definizione, non è contrassegnato come serializzabile.Se hai davvero bisogno che questi dati vengano serializzati, potresti doverli convertire in una classe personalizzata contrassegnata come tale.

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

Mi sono imbattuto in questo problema cercando di eseguire la serializzazione binaria in .net standard 2.0.Alla fine ho risolto il problema utilizzando un custom SurrogateSelector E SerializationBinder.

IL TypeSerializationBinder era necessario perché il framework aveva problemi di risoluzione System.RuntimeType prima che arrivasse SurrogateSelector.Tuttavia, non capisco davvero perché il tipo debba essere risolto prima di questo passaggio ...

Ecco il codice:

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

Esempio di utilizzo:

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);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top