RSD Helper in C#

3/19/2009
I guess I should start out by saying what the RSD format is and what it's used for... One of the main issues that developers have had is the inability to really connect to and interact with web sites without knowing intimate info about the way that site was set up. So eventually, someone who was annoyed with that fact, came up with a file format called RSD. RSD stands for Really Simple Discovery. RSD has one purpose, to let applications know what services are available on a website and where they are located. And while the format is simple, I decided to create a couple classes to help out.

RSD.cs

   1: /*
   2: Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
   3: 
   4: Permission is hereby granted, free of charge, to any person obtaining a copy
   5: of this software and associated documentation files (the "Software"), to deal
   6: in the Software without restriction, including without limitation the rights
   7: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8: copies of the Software, and to permit persons to whom the Software is
   9: furnished to do so, subject to the following conditions:
  10: 
  11: The above copyright notice and this permission notice shall be included in
  12: all copies or substantial portions of the Software.
  13: 
  14: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20: THE SOFTWARE.*/
  21:  
  22: #region Usings
  23: using System;
  24: using System.Text;
  25: using System.Xml;
  26: #endregion
  27:  
  28: namespace Utilities.FileFormats.RSD
  29: {
  30:     /// <summary>
  31:     /// Basic helper for RSD
  32:     /// </summary>
  33:     public class RSD
  34:     {
  35:         #region Constructor
  36:  
  37:         /// <summary>
  38:         /// Constructor
  39:         /// </summary>
  40:         public RSD()
  41:         {
  42:             APIs = new APIs();
  43:         }
  44:  
  45:         /// <summary>
  46:         /// Constructor
  47:         /// </summary>
  48:         /// <param name="FileContent">Content of the RSD file</param>
  49:         public RSD(string FileContent)
  50:         {
  51:             XmlDocument Document = new XmlDocument();
  52:             Document.LoadXml(FileContent);
  53:             foreach (XmlNode Children in Document.ChildNodes)
  54:             {
  55:                 if (Children.Name.Equals("RSD", StringComparison.CurrentCultureIgnoreCase))
  56:                 {
  57:                     foreach (XmlNode Child in Children.ChildNodes)
  58:                     {
  59:                         if (Child.Name.Equals("service", StringComparison.CurrentCultureIgnoreCase))
  60:                         {
  61:                             foreach (XmlNode ServiceChild in Child.ChildNodes)
  62:                             {
  63:                                 if (ServiceChild.Name.Equals("engineName", StringComparison.CurrentCultureIgnoreCase))
  64:                                 {
  65:                                     EngineName = ServiceChild.InnerText;
  66:                                 }
  67:                                 else if (ServiceChild.Name.Equals("engineLink", StringComparison.CurrentCultureIgnoreCase))
  68:                                 {
  69:                                     EngineLink = ServiceChild.InnerText;
  70:                                 }
  71:                                 else if (ServiceChild.Name.Equals("homePageLink", StringComparison.CurrentCultureIgnoreCase))
  72:                                 {
  73:                                     HomePageLink = ServiceChild.InnerText;
  74:                                 }
  75:                                 else if (ServiceChild.Name.Equals("apis", StringComparison.CurrentCultureIgnoreCase))
  76:                                 {
  77:                                     APIs = new APIs((XmlElement)ServiceChild);
  78:                                 }
  79:                             }
  80:                         }
  81:                     }
  82:                 }
  83:             }
  84:         }
  85:         #endregion
  86:  
  87:         #region Public Properties
  88:         /// <summary>
  89:         /// Engine name
  90:         /// </summary>
  91:         public string EngineName { get; set; }
  92:  
  93:         /// <summary>
  94:         /// Link to the engine
  95:         /// </summary>
  96:         public string EngineLink { get; set; }
  97:  
  98:         /// <summary>
  99:         /// Link to the home page
 100:         /// </summary>
 101:         public string HomePageLink { get; set; }
 102:  
 103:         /// <summary>
 104:         /// API definitions
 105:         /// </summary>
 106:         public APIs APIs { get; set; }
 107:  
 108:         #endregion
 109:  
 110:         #region Public Overridden Functions
 111:  
 112:         /// <summary>
 113:         /// Outputs the RSD file
 114:         /// </summary>
 115:         /// <returns>return the properly formatted RSD file</returns>
 116:         public override string ToString()
 117:         {
 118:             StringBuilder Builder = new StringBuilder("<?xml version=\"1.0\" encoding=\"utf-8\"?><rsd version=\"1.0\"><service><engineName>");
 119:             Builder.Append(EngineName).Append("</engineName><engineLink>").Append(EngineLink).Append("</engineLink><homePageLink>")
 120:                 .Append(HomePageLink).Append("</homePageLink>");
 121:             Builder.Append(APIs.ToString());
 122:             Builder.Append("</service></rsd>");
 123:             return Builder.ToString();
 124:         }
 125:  
 126:         #endregion
 127:     }
 128: }

APIs.cs

   1: /*
   2: Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
   3: 
   4: Permission is hereby granted, free of charge, to any person obtaining a copy
   5: of this software and associated documentation files (the "Software"), to deal
   6: in the Software without restriction, including without limitation the rights
   7: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8: copies of the Software, and to permit persons to whom the Software is
   9: furnished to do so, subject to the following conditions:
  10: 
  11: The above copyright notice and this permission notice shall be included in
  12: all copies or substantial portions of the Software.
  13: 
  14: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20: THE SOFTWARE.*/
  21:  
  22: #region Usings
  23: using System;
  24: using System.Collections.Generic;
  25: using System.Text;
  26: using System.Xml;
  27: #endregion
  28:  
  29: namespace Utilities.FileFormats.RSD
  30: {
  31:     /// <summary>
  32:     /// APIs for the RSD format
  33:     /// </summary>
  34:     public class APIs
  35:     {
  36:         #region Constructor
  37:         /// <summary>
  38:         /// Constructor
  39:         /// </summary>
  40:         public APIs()
  41:         {
  42:             APIList = new List<API>();
  43:         }
  44:  
  45:         /// <summary>
  46:         /// Constructor
  47:         /// </summary>
  48:         /// <param name="Element">Element containing the info</param>
  49:         public APIs(XmlElement Element)
  50:         {
  51:             APIList = new List<API>();
  52:             foreach (XmlNode Children in Element.ChildNodes)
  53:             {
  54:                 if (Children.Name.Equals("API", StringComparison.CurrentCultureIgnoreCase))
  55:                 {
  56:                     APIList.Add(new API((XmlElement)Children));
  57:                 }
  58:             }
  59:         }
  60:         #endregion
  61:  
  62:         #region Public Properties
  63:         /// <summary>
  64:         /// List of APIs
  65:         /// </summary>
  66:         public List<API> APIList { get; set; }
  67:  
  68:         #endregion
  69:  
  70:         #region Public Overridden Function
  71:  
  72:         /// <summary>
  73:         /// To string function
  74:         /// </summary>
  75:         /// <returns>APIs list</returns>
  76:         public override string ToString()
  77:         {
  78:             StringBuilder Builder = new StringBuilder();
  79:             Builder.Append("<apis>");
  80:             foreach (API CurrentAPI in APIList)
  81:             {
  82:                 Builder.Append(CurrentAPI.ToString());
  83:             }
  84:             Builder.Append("</apis>");
  85:             return Builder.ToString();
  86:         }
  87:  
  88:         #endregion
  89:     }
  90: }

API.cs

   1: /*
   2: Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
   3: 
   4: Permission is hereby granted, free of charge, to any person obtaining a copy
   5: of this software and associated documentation files (the "Software"), to deal
   6: in the Software without restriction, including without limitation the rights
   7: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8: copies of the Software, and to permit persons to whom the Software is
   9: furnished to do so, subject to the following conditions:
  10: 
  11: The above copyright notice and this permission notice shall be included in
  12: all copies or substantial portions of the Software.
  13: 
  14: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20: THE SOFTWARE.*/
  21:  
  22: #region Usings
  23: using System.Xml;
  24: #endregion
  25:  
  26: namespace Utilities.FileFormats.RSD
  27: {
  28:     /// <summary>
  29:     /// API class
  30:     /// </summary>
  31:     public class API
  32:     {
  33:         #region Constructor
  34:         /// <summary>
  35:         /// Constructor
  36:         /// </summary>
  37:         public API()
  38:         {
  39:         }
  40:  
  41:         /// <summary>
  42:         /// Constructor
  43:         /// </summary>
  44:         /// <param name="Element">Element containing the API info</param>
  45:         public API(XmlElement Element)
  46:         {
  47:             if (Element.Attributes["name"] != null)
  48:             {
  49:                 Name = Element.Attributes["name"].Value;
  50:             }
  51:             if (Element.Attributes["preferred"] != null)
  52:             {
  53:                 Preferred = bool.Parse(Element.Attributes["preferred"].Value);
  54:             }
  55:             if (Element.Attributes["apiLink"] != null)
  56:             {
  57:                 APILink = Element.Attributes["apiLink"].Value;
  58:             }
  59:             if (Element.Attributes["blogID"] != null)
  60:             {
  61:                 BlogID = Element.Attributes["blogID"].Value;
  62:             }
  63:         }
  64:         #endregion
  65:  
  66:         #region Public Properties
  67:         /// <summary>
  68:         /// Name of the API
  69:         /// </summary>
  70:         public string Name { get; set; }
  71:  
  72:         /// <summary>
  73:         /// Is this API preferred?
  74:         /// </summary>
  75:         public bool Preferred { get; set; }
  76:  
  77:         /// <summary>
  78:         /// Link to the API
  79:         /// </summary>
  80:         public string APILink { get; set; }
  81:  
  82:         /// <summary>
  83:         /// Link to the blog
  84:         /// </summary>
  85:         public string BlogID { get; set; }
  86:  
  87:         #endregion
  88:  
  89:         #region Public Overridden Function
  90:  
  91:         /// <summary>
  92:         /// To string function
  93:         /// </summary>
  94:         /// <returns>The API info</returns>
  95:         public override string ToString()
  96:         {
  97:             return "<api name=\"" + Name + "\" preferred=\"" + Preferred.ToString() + "\" apiLink=\"" + APILink + "\" blogID=\"" + BlogID + "\"/>";
  98:         }
  99:  
 100:         #endregion
 101:     }
 102: }

There are only three classes with minimal code. The basic RSD file is really just an XML doc that consists of only a couple fields. Specifically the file has the name and link to the engine that created the RSD file, the link to your website/blog, and the list of APIs. The APIs themselves only contain the name, whether it is preferred (so a client app knows which item to try first), the link to the API, and the blog ID (usually just the link to the blog and can even be empty). That's it really. It would be more interesting if there were a couple fields that needed explaining but it's really simple (go figure considering the name).

Now finding an RSD file takes a bit more work. For that, you need to either look in the header of the web page/site to find a link tag like the following:

   1: <link rel="EditURI" type="application/rsd+xml" title="RSD" href="Link to the RSD file" /> 

And if that isn't there, the default is http://www.whatever the website is.com/rsd.xml... And if it isn't there, there is no RSD file (or at least not one that you're going to find). Anyway, try out the code, leave feedback, and happy coding.



Comments