XAttribute and null values
It turns out that for whatever the reason to the constructor for XAttribute does not accept null values for the value parameter. I think this makes sense even though the XElement constructor will accept null values for the value parameter – mostly because it can still function; a null value for an attribute would be nothing not ‘attributeName=”"‘.
The quick and dirty: there’s an easy solution, use an extension method.
public static class AddXAttribute
{
public static XElement NewXAttribute(this XElement xa, XName name, Object value)
{
if (xa != null && value != null)
{
xa.Add(new XAttribute(name, value));
}
return xa;
}
}
Now to explain. Say we have a pretty generic BO for which we want to be able to generate some XML.
public class BusinessObject
{
public int Id { get; set; }
public String Name { get; set; }
public String Description { get; set; }
}
private static String BuildXML(List<BusinessObject> data)
{
XDocument xDoc = null;
try
{
xDoc = new XDocument(
new XElement("Root",
from d in data
select new XElement("BO",
new XAttribute("Id", d.Id),
new XAttribute("Name", d.Name),
new XAttribute("Desc", d.Description)
)
)
);
}
catch (Exception ex)
{
Console.WriteLine("Exception caught: " + ex.Message);
return String.Empty;
}
return xDoc.ToString();
}
If we were try and run the BuildXML method with a set of data that included a null in the Name or Description properties then an exception would occur, “Value cannot be null.” This seems like it would be a pretty standard why of using LINQ expressions to build an XML document as simple as possible. We don’t really care about the nulls – we just want them handled. The answer is to use the NewXAttribute method from above as an extension method. The new BuildXML (call it BuildXML2 for now) would be very similar.
private static String BuildXML2(List<BusinessObject> data)
{
XDocument xDoc = null;
try
{
xDoc = new XDocument(
new XElement("Root",
from d in data
select (new XElement("BO"))
.NewXAttribute("Id", d.Id)
.NewXAttribute("Name", d.Name)
.NewXAttribute("Desc", d.Description)
)
);
}
catch (Exception ex)
{
Console.WriteLine("Exception caught: " + ex.Message);
return String.Empty;
}
return xDoc.ToString();
}
You can still chain together expressions, so the code result is not terribly different than what you would expect. The one difference being that any child data would have to be declared before you would add the attributes. At least the way I think of the data when I’m building it that’s backwards.
I’ve included the entire sample below for completeness.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var data = new List<BusinessObject>();
// get some data
data.Add(new BusinessObject()
{
Id = 1,
Name = "One",
Description = "First One"
});
data.Add(new BusinessObject()
{
Id = 2,
Name = "Two",
Description = "Second One"
});
data.Add(new BusinessObject()
{
Id = 3,
Name = "Three",
Description = "And the third."
});
String xml;
Console.WriteLine("==== Standard =============");
xml = BuildXML(data);
Console.WriteLine(xml);
Console.WriteLine("===========================");
// we need some null data
data[2].Description = null;
Console.WriteLine("==== Standard w/ null data=");
xml = BuildXML(data);
Console.WriteLine(xml);
Console.WriteLine("===========================");
Console.WriteLine("==== New Extention ========");
xml = BuildXML2(data);
Console.WriteLine(xml);
Console.WriteLine("===========================");
Console.ReadLine();
}
private static String BuildXML(List<BusinessObject> data)
{
XDocument xDoc = null;
try
{
xDoc = new XDocument(
new XElement("Root",
from d in data
select new XElement("BO",
new XAttribute("Id", d.Id),
new XAttribute("Name", d.Name),
new XAttribute("Desc", d.Description)
)
)
);
}
catch (Exception ex)
{
Console.WriteLine("Exception caught: " + ex.Message);
return String.Empty;
}
return xDoc.ToString();
}
private static String BuildXML2(List<BusinessObject> data)
{
XDocument xDoc = null;
try
{
xDoc = new XDocument(
new XElement("Root",
from d in data
select (new XElement("BO"))
.NewXAttribute("Id", d.Id)
.NewXAttribute("Name", d.Name)
.NewXAttribute("Desc", d.Description)
)
);
}
catch (Exception ex)
{
Console.WriteLine("Exception caught: " + ex.Message);
return String.Empty;
}
return xDoc.ToString();
}
}
public class BusinessObject
{
public int Id { get; set; }
public String Name { get; set; }
public String Description { get; set; }
}
public static class AddXAttribute
{
public static XElement NewXAttribute(this XElement xa, XName name, Object value)
{
if (xa != null && value != null)
{
xa.Add(new XAttribute(name, value));
}
return xa;
}
}
}