serialize - serializar json c#
¿Puedo serializar un objeto tipo C#? (5)
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 forma de serializar el objeto Type
? Tenga en cuenta que no estoy tratando de serializar el StringBuilder
sí mismo, sino el objeto Type
que contiene los metadatos sobre la clase StringBuilder
.
Acabo de ver su definición, no está marcado como Serializable. Si realmente necesita que se serialicen estos datos, es posible que deba convertirlos a una clase personalizada que esté 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)]
De acuerdo con la documentación de MSDN de System.Type [1], usted debería poder serializar el objeto System.Type. Sin embargo, como el error se refiere explícitamente a System.Text.StringBuilder, es probable que la clase que está causando el error de serialización.
[1] Tipo de clase (sistema) - http://msdn.microsoft.com/en-us/library/system.type.aspx
No sabía que un objeto Type podría crearse con solo una cadena que contenga el nombre completo. Para obtener el nombre completo, puede usar lo siguiente:
string typeName = typeof (StringBuilder).FullName;
A continuación, puede persistir esta cadena como sea necesario, luego reconstruya el tipo como este:
Type t = Type.GetType(typeName);
Si necesita crear una instancia del tipo, puede hacer esto:
object o = Activator.CreateInstance(t);
Si comprueba el valor de o.GetType (), será StringBuilder, tal como cabría esperar.
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 tiene que hacer es declarar la variable como un tipo serializable, y desde ese momento puede referirse a él como tipo de sistema.
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 el uso de AssemblyQualifiedName en lugar de Type.FullName, vea el comentario de @GreyCloud
Brian''s respuesta Brian''s funciona bien si el tipo está en el mismo ensamble que la llamada (como señaló GreyCloud en uno de los comentarios). Por lo tanto, si el tipo está en otro ensamblaje, debe usar AssemblyQualifiedName como también señaló GreyCloud.
Sin embargo, como AssemblyQualifiedName guarda la versión, si sus ensamblajes 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;
}
Estoy publicando esto en caso de que alguien lo necesite.