.NET / C #: использование RSS.NET с лентами переполнения стека: как обрабатывать специальные свойства элементов RSS? - PullRequest
2 голосов
/ 28 ноября 2009

Я пишу оболочку API переполнения стека , в настоящее время на http://soapidotnet.googlecode.com/. У меня есть несколько вопросов о парсинге SO RSS-каналов.

Я решил использовать RSS.NET для разбора RSS, но у меня есть несколько вопросов о моем коде (который я изложил ниже в этом посте).


Мои вопросы:

Прежде всего, правильно ли я анализирую эти атрибуты ? У меня есть класс с именем Question, который имеет эти свойства.

Далее, как я могу проанализировать <re:rank> свойство RSS (используется для количества голосов)? Я не уверен, как RSS.NET позволяет нам делать это. Насколько я понимаю, это элемент с настраиваемым пространством имен.

Наконец, мне нужно добавить все свойства вручную, как в настоящее время в моем коде? Является ли их какая-то десериализация , которую можно использовать?


Код:

Ниже приведен мой текущий код для анализа последних фидов вопросов:

   /// <summary>
    /// Utilises recent question feeds to obtain recently updated questions on a certain site.
    /// </summary>
    /// <param name="site">Trilogy site in question.</param>
    /// <returns>A list of objects of type Question, which represents the recent questions on a trilogy site.</returns>
    public static List<Question> GetRecentQuestions(TrilogySite site)
    {
        List<Question> RecentQuestions = new List<Question>();
        RssFeed feed = RssFeed.Load(string.Format("http://{0}.com/feeds",GetSiteUrl(site)));
        RssChannel channel = (RssChannel)feed.Channels[0];
        foreach (RssItem item in channel.Items)
        {
            Question toadd = new Question();
            foreach(RssCategory cat in item.Categories)
            {
                toadd.Categories.Add(cat.Name);
            }
            toadd.Author = item.Author;
            toadd.CreatedDate = ConvertToUnixTimestamp(item.PubDate).ToString();
            toadd.Id = item.Link.Url.ToString();
            toadd.Link = item.Link.Url.ToString();
            toadd.Summary = item.Description;

            //TODO: OTHER PROPERTIES
            RecentQuestions.Add(toadd);
        }
        return RecentQuestions;
    }

Вот код этого SO RSS-канала:

<feed xmlns="http://www.w3.org/2005/Atom" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" xmlns:re="http://purl.org/atompub/rank/1.0"> 
    <title type="text">Top Questions - Stack Overflow</title> 
    <link rel="self" href="http://stackoverflow.com/feeds" type="application/atom+xml" /> 
    <link rel="alternate" href="http://stackoverflow.com/questions" type="text/html" /> 
    <subtitle>most recent 30 from stackoverflow.com</subtitle> 
    <updated>2009-11-28T19:26:49Z</updated> 
    <id>http://stackoverflow.com/feeds</id> 
    <creativeCommons:license>http://www.creativecommons.org/licenses/by-nc/2.5/rdf</creativeCommons:license> 

    <entry> 
        <id>/957828/usrednenie-uglov-opyat</id> 
        <re:rank scheme="http://stackoverflow.com">0</re:rank> 
        <title type="text">Averaging angles... Again</title> 
        <category scheme="http://stackoverflow.com/feeds/tags" term="algorithm"/><category scheme="http://stackoverflow.com/feeds/tags" term="math"/><category scheme="http://stackoverflow.com/feeds/tags" term="geometry"/><category scheme="http://stackoverflow.com/feeds/tags" term="calculation"/> 
        <author><name>Lior Kogan</name></author> 
        <link rel="alternate" href="/957828/usrednenie-uglov-opyat" /> 
        <published>2009-11-28T19:19:13Z</published> 
        <updated>2009-11-28T19:26:39Z</updated> 
        <summary type="html"> 
            &lt;p&gt;I want to calculate the average of a set of angles.&lt;/p&gt;

&lt;p&gt;I know it has been discussed before (several times). The accepted answer was &lt;strong&gt;Compute unit vectors from the angles and take the angle of their average&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However this answer defines the average in a non intuitive way. The average of 0, 0 and 90 will be &lt;strong&gt;atan( (sin(0)+sin(0)+sin(90)) / (cos(0)+cos(0)+cos(90)) ) = atan(1/2)= 26.56 deg&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;I would expect the average of 0, 0 and 90 to be 30 degrees.&lt;/p&gt;

&lt;p&gt;So I think it is fair to ask the question again: How would you calculate the average, so such examples will give the intuitive expected answer.&lt;/p&gt;

        </summary> 
    </entry> 

и т.д.

Вот мой класс вопросов, если он поможет:

    /// <summary>
    /// Represents a question.
    /// </summary>
    public class Question : Post //TODO: Have Question and Answer derive from Post
    {

        /// <summary>
        /// # of favorites.
        /// </summary>
        public double FavCount { get; set; }

        /// <summary>
        /// # of answers.
        /// </summary>
        public double AnswerCount { get; set; }

        /// <summary>
        /// Tags.
        /// </summary>
        public string Tags { get; set; }

    }


/// <summary>
    /// Represents a post on Stack Overflow (question, answer, or comment).
    /// </summary>
    public class Post
    {
        /// <summary>
        /// Id (link)
        /// </summary>
        public string Id { get; set; }
        /// <summary>
        /// Number of votes.
        /// </summary>
        public double VoteCount { get; set; }
        /// <summary>
        /// Number of views.
        /// </summary>
        public double ViewCount { get; set; }
        /// <summary>
        /// Title.
        /// </summary>
        public string Title { get; set; }
        /// <summary>
        /// Created date of the post (expressed as a Unix timestamp)
        /// </summary>
        public string CreatedDate
        {

            get
            {
                return CreatedDate;
            }
            set
            {
                CreatedDate = value;
                dtCreatedDate = StackOverflow.ConvertFromUnixTimestamp(StackOverflow.ExtractTimestampFromJsonTime(value));

            }

        }
        /// <summary>
        /// Created date of the post (expressed as a DateTime)
        /// </summary>
        public DateTime dtCreatedDate { get; set; }
        /// <summary>
        /// Last edit date of the post (expressed as a Unix timestamp)
        /// </summary>
        public string LastEditDate
        {

            get
            {
                return LastEditDate;
            }
            set
            {
                LastEditDate = value;
                dtLastEditDate = StackOverflow.ConvertFromUnixTimestamp(StackOverflow.ExtractTimestampFromJsonTime(value));

            }

        }
        /// <summary>
        /// Last edit date of the post (expressed as a DateTime)
        /// </summary>
        public DateTime dtLastEditDate { get; set; }
        /// <summary>
        /// Author of the post.
        /// </summary>
        public string Author { get; set; }
        /// <summary>
        /// HTML of the post.
        /// </summary>
        public string Summary { get; set; }
        /// <summary>
        /// URL of the post.
        /// </summary>
        public string Link { get; set; }
        /// <summary>
        /// RSS Categories (or tags) of the post.
        /// </summary>
        public List<string> Categories { get; set; }

    }

Заранее спасибо! Кстати, пожалуйста, помогите проекту библиотеки! :)

1 Ответ

10 голосов
/ 08 декабря 2009

Во-первых, я никогда не использовал RSS.NET, но мне было интересно, осознали ли вы, что у .NET Framework есть свои собственные API-интерфейсы RSS в пространстве имен System.ServiceModel.Syncidation. Класс SyndicationFeed является отправной точкой для этого.

Чтобы ответить на ваш вопрос, я написал небольшой пример, который берет канал для этого вопроса и записывает title , author , id и rank (интересующий вас элемент расширения) для консоли. Это должно показать, насколько прост этот API и как получить доступ к rank .

// load the raw feed
using (var xmlr = XmlReader.Create("https://stackoverflow.com/feeds/question/1813559"))
{
    // get the items within a feed
    var feedItems = SyndicationFeed
                        .Load(xmlr)
                        .GetRss20Formatter()
                        .Feed
                        .Items;

    // print out details about each item in the feed
    foreach (var item in feedItems)
    {
        Console.WriteLine("Title: {0}", item.Title.Text); 
        Console.WriteLine("Author: {0}", item.Authors.First().Name);
        Console.WriteLine("Id: {0}", item.Id);

        // the extensions assume that there can be more than one value, so get
        // the first or default value (default == 0)
        int rank = item.ElementExtensions
                        .ReadElementExtensions<int>("rank", "http://purl.org/atompub/rank/1.0")
                        .FirstOrDefault();

        Console.WriteLine("Rank: {0}", rank);  
    }
}

Приведенный выше код приводит к записи следующего сообщения в консоль ...

Title: .NET / C #: Использование RSS.NET с лентами переполнения стека: как обрабатывать специальные свойства элементов RSS

Автор: Максим З.

Id: .NET / C #: Использование RSS.NET с лентами переполнения стека: как обрабатывать специальные свойства элементов RSS?

Ранг: 0

Для получения дополнительной информации о классе SyndicationFeed перейдите по этой ссылке ...

http://msdn.microsoft.com/en-us/library/system.servicemodel.syndication.syndicationfeed.aspx

Некоторые примеры чтения и записи расширенных значений из RSS-каналов приведены здесь ...

http://msdn.microsoft.com/en-us/library/bb943475.aspx

Что касается создания ваших экземпляров Вопроса, я не уверен, что с сериализацией можно быстро выиграть. Я бы, наверное, написал твой код примерно так ...

var questions = from item in feedItems
                select
                    new Question
                        {
                            Title = item.Title.Text,
                            Author = item.Authors.First().Name,
                            Id = item.Id,
                            Rank = item.ElementExtensions.ReadElementExtensions<int>(
                                "rank", "http://purl.org/atompub/rank/1.0").FirstOrDefault()
                        };

... но он делает то же самое.

Для вышеперечисленного необходимо установить библиотеки .NET 3.5. Следующее не делает, но требует C # 3.5 (который создаст сборки, предназначенные для .NET 2.0)

Я хотел бы предложить вам одну вещь: не создавайте пользовательские типы, а вместо этого пишите методы расширения для типа SyndicationItem. Если вы позволяете своим пользователям иметь дело с SyndicationType (тип, который поддерживается, понимается, документируется и т. Д.), Но добавляете методы расширения для облегчения доступа к специфическим свойствам SO, то вы облегчаете жизнь пользователя, и они всегда могут использовать API SyndicationItem, когда ваш ТАК расширения не делают то, что они хотят. Так, например, если вы написали этот метод расширения ...

public static class SOExtensions
{
    public static int Rank(this SyndicationItem item)
    {
        return item.ElementExtensions
                   .ReadElementExtensions<int>("rank", "http://purl.org/atompub/rank/1.0")
                   .FirstOrDefault();
    }
}

... вы можете получить доступ к званию синдикационного элемента, как это ...

Console.WriteLine("Rank: {0}", item.Rank());  

... и когда SO добавляет какое-либо другое свойство расширения в фид, который вы не указали для пользователя вашего API, может вернуться к просмотру коллекции ElementExtensions.

Одно окончательное обновление ...

Я не использовал библиотеку Rss.NET, но прочитал онлайн-документы. Из первоначального прочтения этих документов я бы предположил, что нет способа добраться до элемента расширения, к которому вы пытаетесь получить доступ (Ранг элемента). Если API RSS.NET разрешает доступ к xml для данного RssItem (и я не уверен, что это так), то вы могли бы использовать механизм метода расширения для дополнения класса RssItem.

Я считаю SyndicationFeed API очень мощным и очень простым для понимания, поэтому, если вам подходит вариант .NET 3.5, я бы пошел в этом направлении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...