I am trying to use XmlAttributeOverrides to control which class properties appear in the xml after the class has been serialized. It works on properties that are on the "root" class but not on nested properties. Here is a simple example to illustrate what I'm trying to accomplish.
My class hierarchy is as follows:
public class Main
{
public string Name { get; set; }
public Location Address { get; set; }
}
public class Location
{
public string StreetAddress { get; set; }
public Contact ContactInfo{ get; set; }
}
public class Contact
{
public string PhoneNumber { get; set; }
public string EmailAddr { get; set; }
}
When I serialize Main(), I get something like this:
<Main>
<Name></Name>
<Address>
<StreetAddress></StreetAddress>
<ContactInfo>
<PhoneNumber></PhoneNumber>
<EmailAddr></EmailAddr>
</ContactInfo>
</Address>
</Main>
What I am able to do is keep either Name or Address from appearing by using this:
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("Address"));
overrides.Add(typeof(Main), "Address", attribs);
xs = new XmlSerializer(typeof(Main), overrides);
What I need to also be able to do is keep Main.Address.ContactInfo from being serialized SOMETIMES (if it's empty). I tried the following but they didn't work:
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("ContactInfo "));
overrides.Add(typeof(Contact), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Contact), overrides);
and...
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("ContactInfo "));
overrides.Add(typeof(Main.Address.ContactInfo), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Main.Address.ContactInfo), overrides);
I've actually tried a lot more, including using XPath statements to designate the attribute name to target but didn't want to fill this page up with failed attempts. Is what I'm asking even possible by this method?
There are easier ways to achieve what you're looking for.
You said that what you are trying to achieve is to not serialize /Main/Address/ContactInfo
if ContactInfo
contains no data.
If you leave your code as is, it will serialize all of Main's properties, whether they are null or empty or not. The first step, is you need to add a XmlSerializerNamespaces
property to all of your objects or each empty object will be serialized as <myElement xsi:nil="true" />
. This can be accomplished easily, as follows:
public MyXmlElement
{
public MyXmlElement()
{
// Add your own default namespace to your type to prevet xsi:* and xsd:*
// attributes from being generated.
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:myDefaultNamespace") });
}
[XmlElement("MyNullableProperty", IsNullable=false)]
public string MyNullableProperty
{
get
{
return string.IsNullOrWhiteSpace(this._myNullableProperty) ?
null : this._myNullableProperty;
}
set { this._myNullableProperty = value; }
}
[XmlNamespacesDeclaration]
public XmlSerializerNamespaces Namespaces { get { return this._namespaces; } }
private XmlSerializerNamespaces _namespaces;
}
The code above declares a Namespaces
property that holds all the relevant namespaces for the XML object. You should provide a default namespace for all of your objects (modeled after the code above). This prevents the xsi:*
and xsd:*
attributes from being output for your objects when they are serialized. Also, specify that the element is not nullable by using the System.Xml.Serialization.XmlElementAttribute
.
Furthermore, by checking for string.IsNullOrWhiteSpace(someVariable)
and returning null, then the
property will not be serialized when the above has been done.
So, putting this all together for your Location
class:
public class Location
{
// You should have a public default constructor on all types for (de)sereialization.
public Location()
{
this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
new XmlQualifiedName(string.Empty, "urn:myNamespace"); // Default namespace -- prevents xsi:nil="true" from being generated, as well as xsd:* attributes.
});
}
public string StreetAddress
{
// If you don't want <StreetAddress xsi:nil="true" /> to be generated, do this:
get { return string.IsNullOrEmpty(this._streetAddress) ? null : this._streetAddress; }
// Otherwise, if you don't care, just do
// get;
// Only need to implement setter if you don't want xsi:nil="true" to be generated.
set { this._streetAddress = value; }
// Otherwise, just
// set;
}
private string _streetAddress;
[XmlElement("ContactInfo", IsNullable=false)]
public Contact ContactInfo
{
// You must definitely do this to prevent the output of ContactInfo element
// when it's null (i.e. contains no data)
get
{
if (this._contactInfo != null && string.IsNullOrWhiteSpace(this._contactInfo.PhoneNumber) && string.IsNullOrWhiteSpace(this._contactInfo.EmailAddr))
return null;
return this._contactInfo;
}
set { this._contactInfo = value; }
}
private Contact _contactInfo;
[XmlNamespacesDeclarations]
public XmlSerializerNamespaces Namespaces
{
get { return this._namespaces; }
}
private XmlSerializerNamespaces _namespaces;
}
With these changes to your Location
class, the empty ContactInfo
property should no longer be serialized to XML when none of the properties are null, empty, or whitespace, or if ContactInfo
itself is null.
You should make similar changes to your other objects.
See my other stackoverflow answers for more on .NET XML serialization:
- XmlSerializer: remove unnecessary xsi and xsd namespaces
- Omitting all xsi and xsd namespaces when serializing an object in .NET?
- Suppress xsi:nil but still show Empty Element when Serializing in .Net
For anyone else trying to do this with XmlAttributeOverrides, turns out that @user1437872 was very close to finding the answer. Here is the override code to ignore the nested element ContactInfo.
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("ContactInfo"));
overrides.Add(typeof(Address), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Main), overrides);