문제

다음과 같은 방법으로 Type 개체를 직렬화하려고 합니다.

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

이렇게 하면 Serialize 호출에서 다음 예외가 발생합니다.

"System.Text.StringBuilder 유형이 예상되지 않았습니다.xmlinclude 또는 soapinclude 속성을 사용하여 정적으로 알려지지 않은 유형을 지정하십시오. "

직렬화하는 방법이 있나요? Type 물체?나는 직렬화하려고하지 않습니다. StringBuilder 그 자체이지만, Type 에 대한 메타데이터가 포함된 객체 StringBuilder 수업.

도움이 되었습니까?

해결책

정규화된 이름이 포함된 문자열로만 Type 개체를 만들 수 있다는 사실을 몰랐습니다.정규화된 이름을 얻으려면 다음을 사용할 수 있습니다.

string typeName = typeof (StringBuilder).FullName;

그런 다음 필요에 따라 이 문자열을 유지할 수 있으며 다음과 같이 유형을 재구성할 수 있습니다.

Type t = Type.GetType(typeName);

해당 유형의 인스턴스를 생성해야 하는 경우 다음을 수행할 수 있습니다.

object o = Activator.CreateInstance(t);

o.GetType()의 값을 확인하면 예상한 대로 StringBuilder가 됩니다.

다른 팁

나도 같은 문제가 있었고 내 해결책은 SerializedType 클래스를 만드는 것이었습니다.System.Type과 자유롭게 변환되지만 문자열로 직렬화됩니다.여러분이 해야 할 일은 변수를 SerializedType으로 선언하는 것뿐입니다. 그런 다음부터는 이를 System.Type으로 참조할 수 있습니다.

수업은 다음과 같습니다.

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

사용법 예 :

[DataContract]
public class A
{

    ...

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

    ...

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

    ...

}

Type.FullName 대신 AssemblyQualifiedName 사용을 고려할 수도 있습니다. @GreyCloud의 의견을 참조하세요.

브라이언의 유형이 호출과 동일한 어셈블리에 있는 경우 답변이 잘 작동합니다(주석 중 하나에서 지적한 GreyCloud와 같음).따라서 유형이 다른 어셈블리에 있는 경우 AssemblyQualifiedName GreyCloud도 지적했듯이.

그러나 AssemblyQualifiedName 버전을 저장합니다. 어셈블리에 해당 유형이 있는 문자열의 버전과 다른 버전이 있으면 작동하지 않습니다.

내 경우에는 이것이 문제였으며 다음과 같이 해결했습니다.

string typeName = typeof (MyClass).FullName;

Type type = GetTypeFrom(typeName);

object myInstance = Activator.CreateInstance(type);

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

혹시 필요하신 분이 계실까봐 포스팅합니다.

System.Type [1]의 MSDN 설명서에 따르면 System.Type 개체를 직렬화할 수 있어야 합니다.그러나 오류는 명시적으로 System.Text.StringBuilder를 참조하므로 직렬화 오류를 일으키는 클래스일 가능성이 높습니다.

[1] 유형 클래스(시스템) - http://msdn.microsoft.com/en-us/library/system.type.aspx

정의를 살펴보면 직렬화 가능으로 표시되어 있지 않습니다.이 데이터를 직렬화해야 한다면 그렇게 표시된 사용자 정의 클래스로 변환해야 할 수도 있습니다.

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 프레임워크를 해결하는 데 문제가 있었기 때문에 필요했습니다. System.RuntimeType 도착하기 전에 SurrogateSelector.그래도 이 단계 전에 유형을 해결해야 하는 이유를 잘 모르겠습니다.

코드는 다음과 같습니다.

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

사용 예:

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);
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top