11
votes

I have added some nullable value types to my serializable class. I perform a serialization using XmlSerializer but when the value is set to null, I get an empty node with xsi:nil="true". This is the correct behaviour as I have found at Xsi:nil Attribute Binding Support.

Is there a way to switch off this option so that nothing is output when the value type is null?

6

6 Answers

7
votes

I've had the same problem.. here's one of the places i read about handling nullable value types while serializing to XML: http://stackoverflow.com/questions/244953/serialize-a-nullable-int

they mention about using built-in patterns like creating additional properties for nullable value types. like for a property named

public int? ABC

you must either add either public bool ShouldSerializeABC() {return ABC.HasValue;} or public bool ABCSpecified { get { return ABC.HasValue; } }

i was only serializing to xml to send to a sql stored proc, so me too has avoided changing my classes. I'm doing a [not(@xsi:nil)] check on all the nullable elements in my .value() query.

4
votes

I found that the public bool ABCSpecified was the only one that worked with .NET 4.0. I also had to add the XmlIgnoreAttribute

Here was my complete solution to suppress a String named ABC in the Web Reference Resource.cs file:

// backing fields
private string abc;
private bool abcSpecified; // Added this - for client code to control its serialization

// serialization of properties
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public string ABC
{
    get
    {
        return this.abc;
    }
    set
    {
        this.abc= value;
    }
}

// Added this entire property procedure
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool ABCSpecified
{
    get
    {
        return this.abcSpecified;
    }
    set
    {
        this.abcSpecified = value;
    }
}
4
votes

I had to add a ShouldSerialize method to each nullable value.

[Serializable]
public class Parent
{
    public int? Element { get; set; }

    public bool ShouldSerializeElement() => Element.HasValue;
}
1
votes

All the meta methods like Specified or ShouldSerialize is just a bad programming design of microsoft .Net XML structure. It gets even more complicated if you do not have direct access to the classes you want to serialize.

In their serialization method they should just add an attribute like "ignoreNullable".

My current workaround serializes the xml and then just removes all nodes having nil="true" using following function.

/// <summary>
/// Remove optional nullabe xml nodes from given xml string using given scheme prefix.
/// 
/// In other words all nodes that have 'xsi:nil="true"' are being removed from document.
/// 
/// If prefix 'xmlns:xsi' is not found in root element namespace input xml content is returned.
/// </summary>
/// <param name="xmlContent"></param>
/// <param name="schemePrefix">Scheme location prefix</param>
/// <returns></returns>
public static String RemoveNilTrue(String xmlContent, String schemePrefix = "xsi")
{
    XmlDocument xmlDocument = new XmlDocument();
    xmlDocument.LoadXml(xmlContent);

    XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlDocument.NameTable);

    bool schemeExist = false;

    foreach (XmlAttribute attr in xmlDocument.DocumentElement.Attributes)
    {
        if (attr.Prefix.Equals("xmlns", StringComparison.InvariantCultureIgnoreCase)
            && attr.LocalName.Equals(schemePrefix, StringComparison.InvariantCultureIgnoreCase))
        {
            nsMgr.AddNamespace(attr.LocalName, attr.Value);
            schemeExist = true;
            break;
        }
    }

    // scheme exists - remove nodes
    if (schemeExist)
    {
        XmlNodeList xmlNodeList = xmlDocument.SelectNodes("//*[@" + schemePrefix + ":nil='true']", nsMgr);

        foreach (XmlNode xmlNode in xmlNodeList)
            xmlNode.ParentNode.RemoveChild(xmlNode);

        return xmlDocument.InnerXml;
    }
    else
        return xmlContent;
}
1
votes

This is probably the least sophisticated answer, but I solved it for me whith a simple string replace.

.Replace(" xsi:nil=\"true\" ", "");

I'm serializing to string first anyway. I can save to file later.

It keeps all my XmlWriterSettings intact. One other solution I found her messed it up:)

    private static string Serialize<T>(T details)
    {
        var serializer = new XmlSerializer(typeof(T));
        using (var ms = new MemoryStream())
        {
            var settings = new XmlWriterSettings
            {
                Encoding = Encoding.GetEncoding("ISO-8859-1"),
                NewLineChars = Environment.NewLine,
                ConformanceLevel = ConformanceLevel.Document,
                Indent = true,
                OmitXmlDeclaration = true
            };

            using (var writer = XmlWriter.Create(ms, settings))
            {
                serializer.Serialize(writer, details);
                return Encoding.UTF8.GetString(ms.ToArray()).Replace(" xsi:nil=\"true\" ", "");
            }
        }
    }
0
votes

I accomplished it this way:

    private bool retentionPeriodSpecified;
    private Nullable<int> retentionPeriod;

    [XmlElement(ElementName = "retentionPeriod", IsNullable = true, Order = 14)]
    public Nullable<int> RetentionPeriod { get => retentionPeriod; set => retentionPeriod = value; }

    [System.Xml.Serialization.XmlIgnore()]
    public bool RetentionPeriodSpecified
    {
        get { return !(retentionPeriod is null); }
    }