How to exclude null properties when using XmlSerializer

Viewed 44249

I'm serializing a class like this

public MyClass
{
    public int? a { get; set; }
    public int? b { get; set; }
    public int? c { get; set; }
}

All of the types are nullable because I want minimal data stored when serializing an object of this type. However, when it is serialized with only "a" populated, I get the following xml

<MyClass ...>
    <a>3</a>
    <b xsi:nil="true" />
    <c xsi:nil="true" />
</MyClass>

How do I set this up to only get xml for the non null properties? The desired output would be

<MyClass ...>
    <a>3</a>
</MyClass>

I want to exclude these null values because there will be several properties and this is getting stored in a database (yeah, thats not my call) so I want to keep the unused data minimal.

10 Answers

If you make the class you want to serialise implement IXmlSerializable, you can use the following writer. Note, you will need to implement a reader, but thats not too hard.

    public void WriteXml(XmlWriter writer)
    {
        foreach (var p in GetType().GetProperties())
        {
            if (p.GetCustomAttributes(typeof(XmlIgnoreAttribute), false).Any())
                continue;

            var value = p.GetValue(this, null);

            if (value != null)
            {
                writer.WriteStartElement(p.Name);
                writer.WriteValue(value);
                writer.WriteEndElement();
            }
        }
    }

If you can accept the overhead that this would bring, rather than serializing directly to string, write to a LINQ XDocument directly where you can post process the serialization. Using regular expressions like the other answers suggest will be very brittle.

I wrote this method to return the LINQ object, but you can always call ToString() on it.

public XElement XmlSerialize<T>(T obj)
{
    var doc = new XDocument();
    var serializer = new XmlSerializer(typeof(T));
    using (var writer = doc.CreateWriter())
        serializer.Serialize(writer, obj);
    doc.Descendants()
        .Where(x => (bool?)x.Attribute(XName.Get("nil", "http://www.w3.org/2001/XMLSchema-instance")) == true)
        .Remove();
    return doc.Root!;
}
Related