views:

1697

answers:

5

I'm trying to serialize an object to XML that has a number of properties, some of which are readonly.

public Guid Id { get; private set; }

I have marked the class [Serializable] and I have implemented the ISerializable interface.

Below is the code I'm using to serialize my object.

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

Unfortunately it falls over on the first line with this message.

InvalidOperationException was unhandled: Unable to generate a temporary class (result=1). error CS0200: Property or indexer 'MyObject.Id' cannot be assigned to -- it is read only

If I set the Id property to public it works fine. Can someone tell me if I'm doing something, or at least if its even possible?

+2  A: 

You could use the System.Runtime.Serialization.NetDataContractSerializer. It is more powerful and fixes some issues of the classic Xml Serializer.

Note that there are different attributes for this one.

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


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

Edit:

Updat based on Marc's comment: You should probably use System.Runtime.Serialization.DataContractSerializer for your case to get a clean XML. The rest of the code is the same.

Stefan Steinegger
NetDataContractSerializer doesn't write xml... - or rather, it isn't clean xml suitable for external consumption - it has assembly metadata in it.
Marc Gravell
@Marc: Thanks for the hint. It always depends on what one wants to achieve. DataContractSerializer is probably what is expected here.
Stefan Steinegger
+5  A: 

You could use DataContractSerializer (but note you can't use xml attributes - only xml elements):

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

Alternatively, you can implement IXmlSerializable and do everything yourself - but this works with XmlSerializer, at least.

Marc Gravell
+1, thanks for the advise on my answer.
Stefan Steinegger
I've changed my code to use the DataContractSerializer and I've noticed that its still running the GetObjectData method. Am I right in thinking that I can either put attributes on my properties to serialize them, or I can implement the ISerializable interface?
Jon Mitchell
If you implement ISerializable (or is it IXmlSeializable?), you're basically doing all the work yourself...
Marc Gravell
A: 

Read only fields will not be serialized using the XmlSerializer... this is due to the nature og the readonly keyword

From MSDN:

The readonly keyword is a modifier that you can use on fields. When a field declaration includes a readonly modifier, assignments to the fields introduced by the declaration can only occur as part of the declaration or in a constructor in the same class.

So... you would pretty much need to set the fields value in the default constructor...

flalar
I thought that because I had implemented the ISerializable.GetObjectData method the XmlSerializer would use that to get the information I wanted to serialize, and not try and access my read only properties.
Jon Mitchell
XmlSerializer doesn't care about ISerializable - only IXmlSerializable
Marc Gravell
Marc thanks, that makes sense.
Jon Mitchell
A: 

Its not possible with that particular serialization mode (see the other comments for workarounds). If you really want to leave your serialization mode as-is, you have to work around the framework limitations on this one. See this example:

http://en.allexperts.com/q/VB-NET-3306/Class-Serialization.htm

Esentially, mark the property public, but throw an exception if it's accessed at any time other than deserialization.

jvenema
"but throw an exception" - since XmlSerializer doesn't support serialization callbacks, you have no way of knowing...
Marc Gravell
+2  A: 

This guy saved me hours of work implementing custom serialization approches to this exact problem. Short answer: Don't use auto properties getter/setter. Follow this link.

Hope it helps...

Isaac