Recently, I ran into the following error when trying to serialize some SubSonic generated classes.
Cannot serialize member ‘XXX’ of type System.Nullable`1[XXX]. XmlAttribute/XmlText cannot be used to encode complex types.
The SubSonic autogenerated classes cannot serialize nullable types such as DateTime? and GUID?. This is really a .NET serialization problem and not directly related to SubSonic, since the SubSonic library just uses the native .NET code for serialization.
Solution 1
Use SubSonic generateNullableProperties setting
One way to fix this in your SubSonic generated classes it to not generate nullable properties by adding the following to your SubSonic configuration:
generateNullableProperties=”false”
By configuring SubSonic to not generate nullable properties, you’ve fixed your serialization problem, but if you need your values to be null, then you’ll end up with more problems because now, you’ll have to use MyNullablePropertyHasValue method instead of MyNullableProperty.HasValue method.
Here’s what the autogenerated property looks like with generateNullableProperties set to true (default):
[XmlAttribute("MyNullableProperty")] public int? MyNullableProperty { get { return GetColumnValue<int?>("MyNullableProperty"); } set { SetColumnValue("MyNullableProperty", value); } }
Here’s what the autogenerated property looks like with generateNullableProperties set to false.
[XmlAttribute("MyNullableProperty")] public int MyNullableProperty { get { int? myNullableProperty = GetColumnValue<int?>("MyNullableProperty"); if (!myNullableProperty.HasValue) return 0; return myNullableProperty.Value; } set { SetColumnValue("MyNullableProperty", value); } } [XmlIgnore] public bool MyNullablePropertyHasValue { get { return GetColumnValue<int?>("MyNullableProperty") != null; } set { int? myNullableProperty = GetColumnValue<int?>("MyNullableProperty"); if (!value) SetColumnValue("MyNullableProperty", null); else if (value && !propSupplierID.HasValue) SetColumnValue("MyNullableProperty", 0); } }
This looks to be one of the simplest changes, however, now all previous null values will be equal to 0, so that would probably mess up some of your previous logic.
Solution 2
Add ShouldSerialize methods to your partial class code
You can also add a method called ShouldSerialize{XXX} (where XXX is your property name). That method should look like:
public bool ShouldSerializeMyNullableProperty() { return MyNullableProperty.HasValue; }
I believe that this is the best technique, but it also requires that you add one method per nullable property per class generated by SubSonic. That could be a lot of work, or you could modify the base class to automatically generate this method for nullable properties.
Solution 3
Use XML Elements instead of XML Attributes
You can edit the SubSonic code to generate XML Elements instead of XML Attributes. That’s not what I’ve doing here. I’m just going to create a new method that produces an XML output using XML elements for the class.
The following code loops through the object’s properties and writes out properties and their values as a XML elements (you could modify this for XML attributes).
public new string ToXML() { StringBuilder xmlResponse = new StringBuilder(); using (XmlWriter writer = XmlWriter.Create(xmlResponse)) { writer.WriteStartElement(this.GetSchema().ClassName); foreach (TableSchema.TableColumnSetting col in this.GetColumnSettings()) { string colValue = string.Empty; string colName = this.GetSchema().GetColumn(col.ColumnName).PropertyName; if (col.CurrentValue != null) colValue = col.CurrentValue.ToString(); if (string.IsNullOrEmpty(colValue)) { writer.WriteStartElement(colName); writer.WriteEndElement(); } else { writer.WriteElementString(colName, colValue); } } writer.WriteEndElement(); } return xmlResponse.ToString(); }
When doing it this way, you won’t be able to deserialize the XML without writing custom deserialization code. This code can go into your partial class code or just add it to your base class.
The output will look similar to:
<MyClassName>
<MyPropertyName>MyPropertyValue</MyPropertyName>
<MyNullableProperty />
</MyClassName>