Suppress xsi:nil but still show Empty Element when Serializing in .Net
Asked Answered
C

2

11

I have a c# class that has 20+ string properties. I set about a fourth of those to an actual value. I would like to serialize the class and get an output of

<EmptyAttribute></EmptyAttribute>

for a property

public string EmptyAttribute {get;set;}

I do not want the output to be

<EmptyAttribute xsi:nil="true"></EmptyAttribute>

I am using the following class

public class XmlTextWriterFull : XmlTextWriter
{
    public XmlTextWriterFull(string filename) : base(filename,Encoding.UTF8) { }

    public override void WriteEndElement()
    {
        base.WriteFullEndElement();
        base.WriteRaw(Environment.NewLine);
    }
}

so that I can get the full tags. I just don't know how to get rid of the xsi:nil.

Capercaillie answered 10/11, 2009 at 18:23 Comment(2)
I think this is exactly what I need too, but your question is incomplete. Rather than <myObject xsi:nil="true" /> you'd like to get <myObject /> ? If I find an answer I'll be back!Decurion
I posted my answer below check it out and vote it up if it helps you or if you find a better way post it and let me know.Capercaillie
C
-6

I was actually able to figure this out. I know its a bit of a hack in some ways but this is how I got it to work

 System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(header.GetType());
        XmlTextWriterFull writer = new XmlTextWriterFull(FilePath);
        x.Serialize(writer, header);
        writer.Flush();
        writer.BaseStream.Dispose();
        string xml = File.ReadAllText(FilePath);
        xml = xml.Replace(" xsi:nil=\"true\"", "");
        File.WriteAllText(FilePath, xml);

Hope this helps someone else out

Capercaillie answered 24/3, 2010 at 19:14 Comment(3)
-1... This is awful way to deal with XML please avoid such string manipulations. If you really want to strip attributes - use proper XML API...Hollah
@AlexeiLevenkov : The whole purpose of the question was to find out the best way to do this - presumably because he didn't know how to use the API to achieve it. A bit harsh to criticise his not using it. A better response would be to provide an answer which demonstrates proper use of XML API.Vlad
@ChrisRogers - there is already good answer - so comment is here to steer people away form this on. String manipulation is pretty much never good approach to handle XML.Hollah
R
13

The way to have the XmlSerializer serialize a property without adding the xsi:nil="true" attribute is shown below:

[XmlRoot("MyClassWithNullableProp", Namespace="urn:myNamespace", IsNullable = false)]
public class MyClassWithNullableProp
{
    public MyClassWithNullableProp( )
    {
        this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
            new XmlQualifiedName(string.Empty, "urn:myNamespace") // Default Namespace
        });
    }

    [XmlElement("Property1", Namespace="urn:myNamespace", IsNullable = false)]
    public string Property1
    {
        get
        {
            // To make sure that no element is generated, even when the value of the
            // property is an empty string, return null.
            return string.IsNullOrEmpty(this._property1) ? null : this._property1;
        }
        set { this._property1 = value; }
    }
    private string _property1;

    // To do the same for value types, you need a "helper property, as demonstrated below.
    // First, the regular property.
    [XmlIgnore] // The serializer won't serialize this property properly.
    public int? MyNullableInt
    {
        get { return this._myNullableInt; }
        set { this._myNullableInt = value; }
    }
    private int? _myNullableInt;

    // And now the helper property that the serializer will use to serialize it.
    [XmlElement("MyNullableInt", Namespace="urn:myNamespace", IsNullable = false)]
    public string XmlMyNullableInt
    {
       get 
       {
            return this._myNullableInt.HasValue?
                this._myNullableInt.Value.ToString() : null;
       }
       set { this._myNullableInt = int.Parse(value); } // You should do more error checking...
    }

    // Now, a string property where you want an empty element to be displayed, but no
    // xsi:nil.
    [XmlElement("MyEmptyString", Namespace="urn:myNamespace", IsNullable = false)]
    public string MyEmptyString
    {
        get
        {
            return string.IsNullOrEmpty(this._myEmptyString)?
                string.Empty : this._myEmptyString;
        }
        set { this._myEmptyString = value; }
    }
    private string _myEmptyString;

    // Now, a value type property for which you want an empty tag, and not, say, 0, or
    // whatever default value the framework gives the type.
    [XmlIgnore]
    public float? MyEmptyNullableFloat
    {
        get { return this._myEmptyNullableFloat; }
        set { this._myEmptyNullableFloat = value; }
    }
    private float? _myEmptyNullableFloat;

    // The helper property for serialization.
    public string XmlMyEmptyNullableFloat
    {
        get
        {
            return this._myEmptyNullableFloat.HasValue ?
                this._myEmptyNullableFloat.Value.ToString() : string.Empty;
        }
        set
        {
            if (!string.IsNullOrEmpty(value))
                this._myEmptyNullableFloat = float.Parse(value);
        }
    }

    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces Namespaces
    {
        get { return this._namespaces; }
    }
    private XmlSerializerNamespaces _namespaces;
}

Now, instantiate this class and serialize it.

// I just wanted to show explicitly setting all the properties to null...
MyClassWithNullableProp myClass = new MyClassWithNullableProp( ) {
    Property1 = null,
    MyNullableInt = null,
    MyEmptyString = null,
    MyEmptyNullableFloat = null
};

// Serialize it.
// You'll need to setup some backing store for the text writer below...
// a file, memory stream, something...
XmlTextWriter writer = XmlTextWriter(...) // Instantiate a text writer.

XmlSerializer xs = new XmlSerializer(typeof(MyClassWithNullableProp),
    new XmlRootAttribute("MyClassWithNullableProp") { 
        Namespace="urn:myNamespace", 
        IsNullable = false
    }
);

xs.Serialize(writer, myClass, myClass.Namespaces);

After retrieving the contents of the XmlTextWriter, you should have the following output:

<MyClassWithNullableProp>
    <MyEmptyString />
     <MyEmptyNullableFloat />
</MyClassWithNullableProp>

I hope this clearly demonstrates how the built-in .NET Framework XmlSerializer can be used to serialize properties to an empty element, even when the property value is null (or some other value you don't want to serialize). In addition, I have shown how you can make sure that null properties are not serialized at all. One thing to note, if you apply an XmlElementAttribute and set the IsNullable property of that attribute to true, then that property will serialize with the xsi:nil attribute when the property is null (unless overriden somewhere else).

Retinitis answered 26/6, 2012 at 3:4 Comment(1)
This is really an excellent way of ensuring empty elements are generated. I already has the serialization code ready. I was only interested in empty element for string property. All I had to do was add a one liner in the property to check String.IsNullOrEmpty and return String.Empty if true. Cheers!!Severson
C
-6

I was actually able to figure this out. I know its a bit of a hack in some ways but this is how I got it to work

 System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(header.GetType());
        XmlTextWriterFull writer = new XmlTextWriterFull(FilePath);
        x.Serialize(writer, header);
        writer.Flush();
        writer.BaseStream.Dispose();
        string xml = File.ReadAllText(FilePath);
        xml = xml.Replace(" xsi:nil=\"true\"", "");
        File.WriteAllText(FilePath, xml);

Hope this helps someone else out

Capercaillie answered 24/3, 2010 at 19:14 Comment(3)
-1... This is awful way to deal with XML please avoid such string manipulations. If you really want to strip attributes - use proper XML API...Hollah
@AlexeiLevenkov : The whole purpose of the question was to find out the best way to do this - presumably because he didn't know how to use the API to achieve it. A bit harsh to criticise his not using it. A better response would be to provide an answer which demonstrates proper use of XML API.Vlad
@ChrisRogers - there is already good answer - so comment is here to steer people away form this on. String manipulation is pretty much never good approach to handle XML.Hollah

© 2022 - 2024 — McMap. All rights reserved.