Recommended hosting
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.