75

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?

Jon Mitchell
  • 3,109
  • 5
  • 24
  • 35

4 Answers4

63

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
  • 927,783
  • 236
  • 2,422
  • 2,784
  • 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 Apr 29 '09 at 15:26
  • If you implement ISerializable (or is it IXmlSeializable?), you're basically doing all the work yourself... – Marc Gravell Apr 30 '09 at 07:28
  • 4
    This worked for me, but later I found out that serializing private members with the DataMemberAttribute only works when running in full trust environment, not in partial trust. A solution is making the member internal instead of private. For details see http://blog.walteralmeida.com/2010/05/wcf-and-datacontract-serialization-internals-and-tips-.html. – Peladao Jul 22 '11 at 18:12
6

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:

Update 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.

Flot2011
  • 4,212
  • 2
  • 37
  • 54
Stefan Steinegger
  • 60,747
  • 15
  • 122
  • 189
  • NetDataContractSerializer doesn't write xml... - or rather, it isn't clean xml suitable for external consumption - it has assembly metadata in it. – Marc Gravell Apr 29 '09 at 15:05
  • @Marc: Thanks for the hint. It always depends on what one wants to achieve. DataContractSerializer is probably what is expected here. – Stefan Steinegger Apr 29 '09 at 15:11
2

Read only fields will not be serialized using the XmlSerializer, this is due to the nature of 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...

abatishchev
  • 92,232
  • 78
  • 284
  • 421
flalar
  • 1,065
  • 3
  • 12
  • 22
  • 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 Apr 29 '09 at 15:11
  • XmlSerializer doesn't care about ISerializable - only IXmlSerializable – Marc Gravell Apr 29 '09 at 15:15
0

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

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

Mong Zhu
  • 20,890
  • 7
  • 33
  • 66
jvenema
  • 42,243
  • 5
  • 64
  • 107
  • 5
    "but throw an exception" - since XmlSerializer doesn't support serialization callbacks, you have no way of knowing... – Marc Gravell Apr 29 '09 at 15:16
  • 1
    You could use `System.Diagnostics.StackTrace` to find out what's calling your property, but I wouldn't recommend such a solution :-) – Louis Somers Dec 31 '12 at 13:30