Just took a stab at creating an aggregate feed - from two separate Atom feeds - sorted descending by published date...
UPDATE: Thanks to Martin Honnen (MVP) over at http://social.msdn.microsoft.com/Forums/en-US/categories/ - combined with a safe XElement.Load(url) helper (and iterator block)... I think the code below is a pretty good approach for aggregating Xml documents (aggregate sitemaps in this case - although this can easily be adapted for Atom or RSS feeds).
The namespace conversion helper below converts elements only and not attributes (although that too can be added).
static void Main(string[] args)
{
XDocument feed = MergeSiteMaps(new List<string>() { "http://www.58bits.com/blog/googleSitemap.ashx", "http://www.58bits.com/otherblog/googleSiteMap.ashx", "http://www.58bits.com/photos/sitemap.xml"});
XNamespace sm = "http://www.sitemaps.org/schemas/sitemap/0.9";
foreach (XElement location in feed.Root.Elements(sm + "url").Elements(sm + "loc"))
{
Console.WriteLine((string)location);
}
}
public static XDocument MergeSiteMaps(IEnumerable<string> urls)
{
XNamespace sm = "http://www.sitemaps.org/schemas/sitemap/0.9";
XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
XNamespace xsd = "http://www.w3.org/2001/XMLSchema";
string schemaLocation = "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd";
//Our container sitemap document
return new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement(sm + "urlset",
new XAttribute(XNamespace.Xmlns + "xsi", xsi),
new XAttribute(XNamespace.Xmlns + "xsd", xsd),
new XAttribute(xsi + "schemaLocation", schemaLocation),
new XElement(sm + "url",
new XElement(sm + "loc", "http://www.58bits.com/"),
new XElement(sm + "lastmod", DateTime.Now.ToString("yyyy-MM-dd")),
new XElement(sm + "changefreq", "monthly"),
new XElement(sm + "priority", "1.0")),
new XElement(sm + "url",
new XElement(sm + "loc", "http://www.58bits.com/default.aspx"),
new XElement(sm + "lastmod", DateTime.Now.ToString("yyyy-MM-dd")),
new XElement(sm + "changefreq", "monthly"),
new XElement(sm + "priority", "1.0")),
GetElements(sm, urls, "url"))
);
}
private static IEnumerable<XElement> GetElements(XNamespace ns, IEnumerable<string> urls, string elementLocalName)
{
XElement source;
foreach (string url in urls)
{
try
{
source = XElement.Load(url);
}
catch (Exception ex)
{
//TODO: Log the Url that failed
string message = ex.Message;
continue;
}
XNamespace defaultNamespace = source.GetDefaultNamespace();
bool differentNamespace = (ns != defaultNamespace);
foreach (XElement element in source.Elements(defaultNamespace + elementLocalName))
{
if (differentNamespace)
ChangeNamespace(ns, element);
yield return element;
}
}
}
private static void ChangeNamespace(XNamespace ns, XElement entry)
{
foreach (XElement e in entry.DescendantsAndSelf())
{
if (e.Name.Namespace != XNamespace.None)
{
e.Name = ns.GetName(e.Name.LocalName);
}
}
}