2009-04-29 25 views
71

Estoy intentando serializar un objeto a XML que tiene varias propiedades, algunas de las cuales son de solo lectura.Serializar datos de miembros privados

public Guid Id { get; private set; } 

He marcado la clase [Serializable] y he implementado la interfaz ISerializable.

A continuación se muestra el código que estoy utilizando para serializar mi objeto.

public void SaveMyObject(MyObject obj) 
{ 
    XmlSerializer serializer = new XmlSerializer(typeof(MyObject)); 
    TextWriter tw = new StreamWriter(_location); 
    serializer.Serialize(tw, obj); 
    tw.Close(); 
} 

Desafortunadamente se cae en la primera línea con este mensaje.

InvalidOperationException era no controlada: No se puede generar una clase temporal (resultado = 1). CS0200 error: la propiedad o indizador MyObject.Id 'no se puede asignar a - que es de sólo lectura

Si fijo la propiedad Id a pública que trabaja muy bien. ¿Puede alguien decirme si estoy haciendo algo, o al menos si es posible?

Respuesta

57

Usted podría utilizar DataContractSerializer (pero tenga en cuenta que no puede utilizar los atributos XML - únicos elementos XML):

using System; 
using System.Runtime.Serialization; 
using System.Xml; 
[DataContract] 
class MyObject { 
    public MyObject(Guid id) { this.id = id; } 
    [DataMember(Name="Id")] 
    private Guid id; 
    public Guid Id { get {return id;}} 
} 
static class Program { 
    static void Main() { 
     var ser = new DataContractSerializer(typeof(MyObject)); 
     var obj = new MyObject(Guid.NewGuid()); 
     using(XmlWriter xw = XmlWriter.Create(Console.Out)) { 
      ser.WriteObject(xw, obj); 
     } 
    } 
} 

Alternativamente, se puede implementar IXmlSerializable y hacer todo usted mismo - pero esto funciona con XmlSerializer, al menos .

+0

+1, gracias por el asesoramiento sobre mi respuesta. –

+0

He cambiado mi código para usar DataContractSerializer y me he dado cuenta de que todavía está ejecutando el método GetObjectData. ¿Estoy en lo cierto al pensar que puedo poner atributos en mis propiedades para serializarlos, o puedo implementar la interfaz ISerializable? –

+0

Si implementa ISerializable (¿o es IXmlSeializable?), Básicamente está haciendo todo el trabajo usted mismo ... –

6

Puede usar el System.Runtime.Serialization.NetDataContractSerializer. Es más potente y soluciona algunos problemas del clásico Serializador Xml.

Tenga en cuenta que hay diferentes atributos para este.

[DataContract] 
public class X 
{ 
    [DataMember] 
    public Guid Id { get; private set; } 
} 


NetDataContractSerializer serializer = new NetDataContractSerializer(); 
TextWriter tw = new StreamWriter(_location); 
serializer.Serialize(tw, obj); 

Editar:

de actualización basado en el comentario de Marc: Probablemente debería utilizar System.Runtime.Serialization.DataContractSerializer para su caso para obtener un XML limpio. El resto del código es el mismo.

+0

NetDataContractSerializer no escribe xml ... - o mejor dicho, no es xml limpio adecuado para consumo externo - tiene metadatos de ensamblaje en él. –

+0

@Marc: gracias por la pista. Siempre depende de lo que uno quiera lograr. DataContractSerializer es probablemente lo que se espera aquí. –

1

Sólo Lectura campos no se serializan con el XmlSerializer, esto es debido a la naturaleza de la readonly palabra clave

De MSDN:

El sólo lectura de palabras clave es un modificador que puede utilizar en los campos. Cuando una declaración de campo incluye un modificador de solo lectura, las asignaciones a los campos introducidos por la declaración solo pueden ocurrir como parte de la declaración o en un constructor en la misma clase.

... Así que sería más o menos que configurar los campos de valores en el constructor por defecto ...

+0

Pensé que porque había implementado el método ISerializable.GetObjectData el XmlSerializer usaría eso para obtener la información que quería serializar, y no trataría de acceder a mis propiedades de solo lectura . –

+0

XmlSerializer no se preocupa por ISerializable, solo IXmlSerializable –

+0

Marc gracias, eso tiene sentido. –

0

No es posible con ese modo particular de serialización (ver los otros comentarios para soluciones). Si realmente desea abandonar su modo de serialización tal como está, debe evitar las limitaciones del marco en este caso.Consulte esto example

Esencialmente, marque la propiedad public, pero eche una excepción si se accede en cualquier momento que no sea la deserialización.

+5

"pero arroje una excepción": como XmlSerializer no es compatible con las devoluciones de llamadas de serialización, no tiene manera de saber ... –

+1

Puede usar 'System.Diagnostics.StackTrace' para averiguar qué llama su propiedad, pero yo no lo haría recomendar una solución así :-) –

Cuestiones relacionadas