More often I’m being asked to provide “SiteMap.xml”s for sites we work on, and up until recently we had been hand rolling the xml and placing them in static documents on our sites. This is fine but seemed a little silly because ASP.NET has a built in SiteMap mechanism. So we decided to do something about using it for auto generating the SiteMap.xml.
My example is using Maarten Balliauw’s awesome MvcSiteMap available at http://mvcsitemap.codeplex.com/ but the conversion code works perfectly fine with the built in SiteMapProvider in ASP.NET.
Using the following class we can generate the XML object representing the SiteMap.
public class SiteMapXMLNode { public enum enChangeFrequency { NoneGiven, Always, Hourly, Daily, Weekly, Monthly, Yearly, Never } [System.Xml.Serialization.XmlElement(ElementName = "loc")] public string URL; #region Last Modified [System.Xml.Serialization.XmlIgnore] public DateTime LastModified; [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElement(ElementName = "lastmod")] public string __LastModified { get { return (LastModified == DateTime.MinValue ? null : string.Format("{0:yyyy-MM-dd}", LastModified)); } set { LastModified = DateTime.MinValue; } //Note: This property will never be deserialized } #endregion #region Change Frequency [System.Xml.Serialization.XmlIgnore] public enChangeFrequency ChangeFrequency; [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElement(ElementName = "changefreq")] public string __ChangeFrequency { get { return (ChangeFrequency == enChangeFrequency.NoneGiven ? null : ChangeFrequency.ToString().ToLower()); } set { ChangeFrequency = enChangeFrequency.NoneGiven; } //Note: This property will never be deserialized } #endregion #region Priority [System.Xml.Serialization.XmlIgnore] public decimal? Priority; [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElement(ElementName = "priority")] public string __Priority { get { return (!Priority.HasValue ? null : string.Format("{0:0.0}", Priority)); } set { Priority = null; } //Note: This property will never be deserialized } #endregion public SiteMapXMLNode() { URL = null; LastModified = DateTime.MinValue; ChangeFrequency = enChangeFrequency.NoneGiven; Priority = null; } public SiteMapXMLNode(System.Web.SiteMapNode node) : this() { UriBuilder uriBuilder = new UriBuilder(System.Web.HttpContext.Current.Request.Url); uriBuilder.Path = node.Url; URL = uriBuilder.ToString(); } } [System.Xml.Serialization.XmlRootAttribute(Namespace = "http://www.sitemaps.org/schemas/sitemap/0.9", IsNullable = false, ElementName = "urlset")] public class SiteMapXML { private List<SiteMapXMLNode> Nodes; #region "urls" [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("url")] public SiteMapXMLNode[] __Nodes { get { return Nodes.ToArray(); } set { Nodes.Clear(); Nodes.AddRange(value); } } #endregion public SiteMapXML() { Nodes = new List<SiteMapXMLNode>(); } public SiteMapXML(System.Web.SiteMapNode rootNode) : this() { Add(rootNode); } public void Add(System.Web.SiteMapNode rootNode) { Nodes.Add(new SiteMapXMLNode(rootNode)); if (rootNode.HasChildNodes) { foreach (System.Web.SiteMapNode node in rootNode.ChildNodes) { if (node.IsAccessibleToUser(System.Web.HttpContext.Current)) Add(node); } } } }
Then we use this action for MVC
public ActionResult SiteMapXML() { ActionResult result = null; SiteMapXML siteMapXML = new SiteMapXML(System.Web.SiteMap.RootNode); using (System.IO.MemoryStream oStream = new System.IO.MemoryStream()) { System.Xml.Serialization.XmlSerializer xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(SiteMapXML)); xmlSerializer.Serialize(oStream, siteMapXML); oStream.Flush(); xmlSerializer = null; System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding(false); result = new ContentResult() { ContentType = "text/xml", Content = UTF8.GetString(oStream.ToArray()), ContentEncoding = UTF8 }; } return result; }
Or this code behind for ASP.NET
protected void Page_Load(object sender, EventArgs e) { Response.Clear(); Response.ContentType = "text/xml"; SiteMapXML siteMapXML = new SiteMapXML(System.Web.SiteMap.RootNode); System.Xml.Serialization.XmlSerializer xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(SiteMapXML)); xmlSerializer.Serialize(Response.OutputStream, siteMapXML); xmlSerializer = null; Response.Flush(); Response.End(); }
It’s really that simple
ToDo: add Attributes and elements to the SiteMap definition so we can take advantage of the additional Attributes in the SiteMap XSD (“lastmod”, “changefreq” and “priority”)