คำตอบ Reading XML with a Namespace via XElement

  • 2 มีนาคม 2553 18:38
     
      มีโค้ด
    I'm working on an application that includes an RSS reader, and came across something I don't know how to handle.

    The XML file is formed like so:
    <?xml version="1.0" encoding="windows-1251"?>
    <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
    	<channel>
    		<title>Feed Title</title>
    		<link>http://feed.net/</link>
    		<language>en</language>
    		<description>A really cool feed</description>
    		<generator>DataLife Engine</generator>
    		<item>
    			<title>Item Title</title>
    			<guid isPermaLink="true">http://feed.net/item.html</guid>
    			<link>http://feed.net/item.html</link>
    			<description><![CDATA[<div align="center">a bunch of HTML in here</div><br /><br />]]></description>
    			<category><![CDATA[Category Name]]></category>
    			<dc:creator>Creator Name</dc:creator>
    			<pubDate>Tue, 02 Mar 2010 17:34:33 +0300</pubDate>
    		</item>
    	</channel>
    </rss>
    The problem I have is this:

    When I attempt to read the creator element with
    NewItem.Creator = thisItem.Element("creator").Value;
    I get an error, because thisItem.Element("creator") is null.  How can I correctly resolve this element?  I've tried
    NewItem.Creator = thisItem.Element("dc:creator").Value;
    but that gives me an error saying that ":" is not a valid character in an element name. 

    I'm guessing I'll have to parse the namespace and prepend it to the element name, but how would I do that?

    Thanks!

    Gabe

ตอบทั้งหมด

  • 2 มีนาคม 2553 18:55
     
     คำตอบ
    Do you know the namespace when writing the code? Then you can simply do
    XNamespace dc = "http://purl.org/dc/elements/1.1/";
    in your code and then use e.g.
    thisItem.Element(dc + "creator).Value
    MVP XML My blog
  • 3 มีนาคม 2553 14:43
     
     
    Yes, that will work if I always know the namespace, but I'm concerned that, since this will be reading RSS feeds "in the wild", that I might not always know the namespace beforehand.
  • 3 มีนาคม 2553 16:06
     
     คำตอบ
    Yes, that will work if I always know the namespace, but I'm concerned that, since this will be reading RSS feeds "in the wild", that I might not always know the namespace beforehand.
    But how do you identify which element(s) you are interested in? A feed can have any kind of elements in any namespace, which ones are you looking for?

    MVP XML My blog
  • 9 มีนาคม 2553 2:42
    ผู้ดูแล
     
     

    Hello Gabe,

     

     

    How is the problem at your side?   If you need any further assistance, please feel free to let us know. 
     

    Have a nice day!

     

     

    Best Regards,
    Lingzhi Sun

    MSDN Subscriber Support in Forum

    If you have any feedback on our support, please contact msdnmg@microsoft.com


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
  • 28 เมษายน 2555 8:36
     
      มีโค้ด

    Hey guys, I'm struggling on this same issue in a Metro xaml + c# app. The sample documentation for rss works great, except when you're feed included dc:creator instead of authors.

    Here's the code I've got so far based on the discussions above.

             foreach (SyndicationItem item in feed.Items)
                    {
                        WriterItem WriterItem = new WriterItem();
                        WriterItem.Title = item.Title.Text;
                        WriterItem.PubDate = item.PublishedDate.DateTime;
                      
                        XNamespace dc = "http://purl.org/dc/elements/1.1/";
                        WriterItem.Author = item(dc + "creator").ToString;
    
                        //WriterItem.Author = item.Authors[0].Name.ToString();
    


    techAU http://techAU.tv

  • 28 เมษายน 2555 10:07
     
      มีโค้ด

    I think you are struggling with a different API, namely the SyndicationFeed/SyndicationItem API. I think the API is complex but poorly documented, I managed to fiddle together a sample nevertheless:

                SyndicationFeed feed;
                using (XmlReader xr = XmlReader.Create("../../XMLFile7.xml"))
                {
                    feed = SyndicationFeed.Load(xr);
                }
    
                const string dcNs = "http://purl.org/dc/elements/1.1/";
    
                foreach (SyndicationItem item in feed.Items)
                {
                    List<SyndicationElementExtension> elements =
                        item.ElementExtensions.Where(el => el.OuterNamespace == dcNs && el.OuterName == "creator").ToList();
                    List<string> creators = new List<string>(elements.Count);
                    
                    foreach (SyndicationElementExtension element in elements)
                    {
                        using (XmlReader xr = element.GetReader())
                        {
                            XElement xEl = XElement.ReadFrom(xr) as XElement;
                            creators.Add(xEl.Value);
                        }
                    }
                    Console.WriteLine("Item {0} has {1} creators: ", item.Title != null ? item.Title.Text : "", creators.Count);
                    foreach (string creator in creators)
                    {
                        Console.WriteLine("{0}", creator);
                    }
                    Console.WriteLine();
                }

    There might be easier ways to read out such elements, unfortunately there is not much documentation on those classes.


    MVP Data Platform Development My blog

  • 28 เมษายน 2555 10:23
     
     คำตอบที่เสนอ มีโค้ด

    Here is a simplification of the code, as far as the LINQ part is concerned:

                SyndicationFeed feed;
                using (XmlReader xr = XmlReader.Create("sheet.rss"))
                {
                    feed = SyndicationFeed.Load(xr);
                }
    
                const string dcNs = "http://purl.org/dc/elements/1.1/";
    
                foreach (SyndicationItem item in feed.Items)
                {
                    List<string> creators = 
                        item
                        .ElementExtensions
                        .Where(el => el.OuterNamespace == dcNs && el.OuterName == "creator")
                        .Select(el => (XElement.ReadFrom(el.GetReader()) as XElement).Value)
                        .ToList();
     
                    Console.WriteLine("Item {0} has {1} creators: ", item.Title != null ? item.Title.Text : "", creators.Count);
                    foreach (string creator in creators)
                    {
                        Console.WriteLine("{0}", creator);
                    }
                    Console.WriteLine();
                }


    MVP Data Platform Development My blog

    • เสนอเป็นคำตอบโดย Martin Honnen 28 เมษายน 2555 10:24
    •  
  • 28 เมษายน 2555 11:48
     
     
    Thanks, but hasn't helped. I agree the SyndicationItems is poorly documented for WinRT xaml c#. Will persist with searching on a solution.

    techAU http://techAU.tv

  • 28 เมษายน 2555 12:16
     
      มีโค้ด

    The code I posted works with .NET 4.0 but it seems the API is different with WinRT.

    I currently have no way here to write/compile/test against WinRT so the following is meant purely as a suggestion based on the MSDN docs:

                foreach (SyndicationItem item in feed.Items)
                {
                    List<string> creators = 
                        item
                        .ElementExtensions
                        .Where(el => el.NodeNamespace = "http://purl.org/dc/elements/1.1/" && el.NodeName == "creator")
                        .Select(el => el.NodeValue)
                        .ToList();
     
                    Console.WriteLine("Item {0} has {1} creators: ", item.Title != null ? item.Title.Text : "", creators.Count);
                    foreach (string creator in creators)
                    {
                        Console.WriteLine("{0}", creator);
                    }
                    Console.WriteLine();
                }


    MVP Data Platform Development My blog

  • 28 เมษายน 2555 12:42
     
     
    Close, but there's no .where parameter in the ElementExtensions. Appreciate your help.

    techAU http://techAU.tv

  • 28 เมษายน 2555 13:09
     
      มีโค้ด

    The Where and Select are LINQ query methods, if that is not supported then try a simple foreach loop along the lines of e.g.

                foreach (SyndicationItem item in feed.Items)
                {
                    List<string> creators = new List<string>();
                    foreach (var el in item.ElementExtensions)
                    {
                       if (el.NodeNamespace == "http://purl.org/dc/elements/1.1/" && el.NodeName == "creator")
                       {
                          creators.Add(el.NodeValue);
                       } 
                    }
     
                    Console.WriteLine("Item {0} has {1} creators: ", item.Title != null ? item.Title.Text : "", creators.Count);
                    foreach (string creator in creators)
                    {
                        Console.WriteLine("{0}", creator);
                    }
                    Console.WriteLine();
                }


    MVP Data Platform Development My blog

  • 28 เมษายน 2555 13:53
     
      มีโค้ด

    That was definitely helpful and my code now doesn't show any errors.. it does compile, but when I seen no author text, I started debugging and seen that after it hit the foreach line the rest was skipped passed. Any ideas? I think we're almost there.

    foreach (SyndicationItem item in feed.Items)
                    {
                        FeedItem feedItem = new FeedItem();
                        feedItem.Title = item.Title.Text;
                        feedItem.PubDate = item.PublishedDate.DateTime;
    
                        //Get Author from dc:creator field in RSS
                        List<string> creators = new List<string>();
                        foreach (var el in item.ElementExtensions)
                        {
                            if (el.NodeNamespace == "http://purl.org/dc/elements/1.1/" && el.NodeName == "creator")
                            {
                               creators.Add(el.NodeValue);
                            }
                        }
                        foreach (string creator in creators)
                        {
                            feedItem.Author = creator;
                        }


    techAU http://techAU.tv

  • 28 เมษายน 2555 14:54
     
     

    Does the feed you want to read in declare the prefix "dc" for the namespace URI "http://purl.org/dc/elements/1.1/" used in the code sample? Or is a different URL declared there?

    And what does the debugger show for

      item.ElementExtensions

    are the any elements contained in that collection? What's it Count property?


    MVP Data Platform Development My blog

  • 28 เมษายน 2555 21:34
     
     

    Hey Martin, yes the feed absolutely uses the ns URI is http://purl.org/dc/elements/1.1/

    As for the return from item.ElementExtensions, it says the count is 6, but as I said passes right over it.

    Happy to provide further info.


    techAU http://techAU.tv

  • 29 เมษายน 2555 9:54
     
     

    As for the return from item.ElementExtensions, it says the count is 6, but as I said passes right over it.

    Happy to provide further info.

    I am not sure what to suggest, if the Count property shows 6 items then I don't understand why a foreach loop does not process them.

    Does using an explicit type with e.g.

      foreach (ISyndicationNode el in item.ElementExtensions) { ... }

    process the items in the collection?

    It might be better to ask in a forum for Metro style app development with C#, hopefully other developers there are more successful on helping with the code than I am by trying to read the documentation.


    MVP Data Platform Development My blog