Update: I was wrong, and private setters do not appear to work (strange, I’m sure I verified this in my test code, but it didn’t work in practice). See below.
It’s been ages since I used XmlSerializer, or even written meaningful amounts of code in C# for that matter, which is why I was utterly stumped by this problem. Try running this code:
[XmlRoot( "test" )]
public class Test
{
private readonly string _string1;
private readonly string _string2;
public Test( string string1, string string2 )
{
_string1 = string1;
_string2 = string2;
}
[XmlAttribute( "attr" )]
public string String1 { get { return _string1; } }
[XmlElement( "element" )]
public string String2 { get { return _string2; } }
}
/// ...
XmlSerializer xs = new XmlSerializer( typeof( Test ), "" );
XmlSerializerNamespaces xsn = new XmlSerializerNamespaces();
xsn.Add("", ""); // Gets rid of redundant xmlns: attributes
StringWriter pw = new StringWriter();
xs.Serialize( pw, new Test( "1", "2" ), xsn );
Console.WriteLine( pw.GetStringBuilder().ToString() );
You’ll get an
InvalidOperationException stating that “Test cannot be serialized because it does not have a parameterless constructor.” A quick look at the documentation (or a search which may lead you to
this post on StackOverflow) will get you the answer: add a parameterless constructor and mark it
private. Run the test code again and this time no exception will be thrown; however, you probably won’t be expecting this result:
<?xml version="1.0" encoding="utf-16"?>
<test />
The solution? Add a setter to each of your serializable properties. Don’t need a setter because the class is immutable? Mark it as private and you’re good to go. Tough – you’re going to need one anyway, private/internal/protected setters don’t appear to work. If you must use XmlSerializer you should throw a NotImplementedException from these setters, but in my opinion the resulting contract clutter implies you should simply avoid XmlSerializer altogether.
Although this behavior makes sense in light of how XmlSerializer can be used for both serialization and de-serialization, what threw me off was that no exception is thrown – the contract doesn’t require a setter property, and the serializer output is corrupt. Beware!