Sep
28
2006
Updated RSS reader class - dealing with "already belongs" error
Posted by admin under
ASP.NET articles
Getting the "A column named 'comments' already belongs to this DataTable" error when running the Simple RSS client ?
You will probably get it if you run it against a "real" blog application - which supports the <comments> tag for one example.
The problem is this:
An RSS typically uses multiple namespaces, for example from by SubText blog:
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
Perfectly fine. It lets you have multiple tags with the same name - but using different namespaces makes them different - from the XML perspective. That is indeed the reason for namespaces - to avoid name collisions.
example below from Scott Guthries blog:
<slash:comments>32</slash:comments>
<comments>http://weblogs.asp.net/scottgu/comments/571505.aspx</comments>
slash:comments is not the same as comments. But as I said - that's from the XML perspective. Now when trying to move that into the dataset with
oXml = new System.Xml.XmlTextReader(m_strAdress);
m_ds.ReadXml(oXml, System.Data.XmlReadMode.Auto);
the ReadXml doesn't care about namespaces - it tries to create a column "comment" for the XML tag "comment" - and also tries to create a column "comment" for the slash:comment tag. Which gives us the name collision.
Anyway - a "real" .NET nerd would probably solve it by XSL transformations - but as I have said before - I solve it as fast as I can in a way I know of - so my solution was to simply alter the XML to remove the ":" stuff (ReadXml understands so much it should use what's on the right side of it as column name - hence the problem).
So. here's the updated GetRSSDataSet:
private DataSet m_ds = null;
public DataSet GetRSSDataSet()
{
if (m_ds == null)
{
System.Xml.XmlTextReader oXml = new System.Xml.XmlTextReader(m_strAdress);
oXml.WhitespaceHandling = System.Xml.WhitespaceHandling.None;
System.Text.StringBuilder oBuilder = new StringBuilder();
bool fContinue = oXml.Read();
while (fContinue)
{
string sName = oXml.Name.Replace(":", "_");
if (oXml.NodeType == System.Xml.XmlNodeType.Element)
{
//Add the element...
oBuilder.Append("<" + sName);
if (oXml.HasAttributes)
{
while (oXml.MoveToNextAttribute())
{
sName = oXml.Name.Replace(":", "_");
oBuilder.AppendFormat(" " + sName + "=\"" + oXml.Value + "\"");
}
}
oBuilder.Append(">");
}
else if (oXml.NodeType == System.Xml.XmlNodeType.Text)
{
oBuilder.Append(System.Web.HttpUtility.HtmlEncode(oXml.Value));
}
else if (oXml.NodeType == System.Xml.XmlNodeType.EndElement)
{
oBuilder.Append("</" + sName + ">");
}
fContinue = oXml.Read();
}
oXml.Close();
string sXmlResult = oBuilder.ToString();
m_ds = new DataSet();
System.IO.StringReader oStringReader = new System.IO.StringReader(sXmlResult);
try
{
m_ds.ReadXml(oStringReader, System.Data.XmlReadMode.Auto);
}
catch (Exception ex)
{
m_ds = null;
}
if (oStringReader != null)
oStringReader.Close();
if (oXml != null)
oXml.Close();
}
return m_ds;
}
In short - my solution is to change the ": " to a "_" in all names. Which will make your columns look like:

The secret is to loop through the original XmlTextReader and essentially copying all items - but first modifying the name when xmltextreader.Name contains a ":".
Don't miss the download for this example.