Prevent <xsi:nil="true"> on Nullable Value Types when Serializing to XML
Asked Answered
H

6

14

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?

Hunker answered 12/2, 2010 at 14:54 Comment(0)
S
10

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;
}
Soup answered 31/8, 2018 at 20:1 Comment(0)
C
9

I've had the same problem.. here's one of the places i read about handling nullable value types while serializing to XML: https://mcmap.net/q/217952/-how-to-serialize-a-nullable-int-without-quot-xsi-nil-quot-being-added-to-the-resulting-xml

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.

Crusted answered 6/6, 2010 at 12:25 Comment(1)
I simply put in an intermediary step to scan the XML after it was serialised and remove any xsi:nil="true" nodes.Hunker
S
5

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;
    }
}
Saxhorn answered 15/4, 2011 at 5:23 Comment(1)
XmlIgnoreAttribute on a "Specified" property is not necessary, as well as a setter on the same property.Jugendstil
P
2

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\" ", "");
            }
        }
    }
Phyte answered 28/2, 2018 at 14:17 Comment(0)
S
1

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;
}
Shanly answered 28/11, 2017 at 16:22 Comment(0)
D
0

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); }
    }
Drayton answered 11/7, 2019 at 15:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.