C # Каков наилучший способ вычисления хэша XML-фида

#c# #xmlreader

#c# #xmlreader

Вопрос:

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

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

 XmlReader reader = XmlReader.Create("http://www.extremetech.com/feed");
SyndicationFeed feed = SyndicationFeed.Load(reader);
  

Комментарии:

1. Каковы последствия столкновения хэшей? То есть предположим, что два документа имеют одинаковый хэш. Что самое худшее, что может случиться?

2. Я провел еще несколько тестов, если это ваш точный канал, в этом канале есть некоторые комментарии, которые периодически меняются, даже если теги xml без комментариев никогда не меняются, поэтому я не думаю, что хэш-подход вообще будет работать

3. @MerickOWA Я думаю, я просто использую идентификатор, который находится в SyndicationItem .. может быть проще 🙂 И таким образом, если заголовок или статья фида будут отредактированы, это не будет проблемой!

4. @superlogical Я добавил еще одну возможность, которая не зависит от хеширования и которая, вероятно, должна работать в целом, хотя она зависит от сервера.

Ответ №1:

Почему бы просто не проверить LastUpdatedTime фида? Это встроенный способ сообщить вам, является ли что-то новым или нет. Вместо хэширования и сохранения хэша вы бы просто отслеживали LastUpdatedTime и периодически сравнивали его с последним LastUpdatedTime:

 using System;
using System.ServiceModel.Syndication;
using System.Xml;

public class MyClass
{
    private static DateTime _lastFeedTime = new DateTime(2011, 10, 10);

    public static void Main()
    {
        XmlReader reader = XmlReader.Create("http://www.extremetech.com/feed");
        SyndicationFeed feed = SyndicationFeed.Load(reader);

        if (feed.LastUpdatedTime.LocalDateTime > _lastFeedTime)
        {
            _lastFeedTime = feed.LastUpdatedTime.LocalDateTime;

            // load feed...
        }
    }
}
  

Комментарии:

1. Да, я подумал об этом, но я просто не знаю, насколько это будет надежно, учитывая, что некоторые каналы могут не обновлять это значение. Но опять же, я могу быть совершенно неправ 🙂 Всегда ли WordPress хорошо справляется с этим? Большинство каналов, которые я хочу проиндексировать, будут основаны на WordPress

2. Значение LastUpdatedTime совершенно ненадежно, поскольку оно зависит от взаимодействующего сервера.

3. Сначала попробуйте использовать DateTime. Не предполагайте и не решайте проблему, если в этом нет необходимости. И да, вы зависите от третьей стороны, соответствующей стандарту, но это происходит постоянно. И я не могу придумать более полезную часть метаданных, чем LastUpdatedTime для соответствия. Несоблюдение должно привести к физическому наказанию. 😉

4. @PaulSasik Я думаю, я просто использую идентификатор, который находится в SyndicationItem

Ответ №2:

Если вы действительно хотите использовать хэш-способ, вы можете сделать следующее:

 var client = new WebClient();

var content = client.DownloadData("http://www.extremetech.com/feed");

var hash = MD5.Create().ComputeHash(content);
var hashString = Convert.ToBase64String(hash);

// you can then compare hashes and if changed load it this way
XmlReader reader = XmlReader.Create(new MemoryStream(content));
  

Конечно, идя таким образом, вы обнаружите любые изменения в содержимом, даже малейшие.

ИМХО, лучший способ — загрузить фид в любом случае и хэшировать только содержимое статей, вы можете хэшировать любую строку следующим образом:

 var toHash = "string to hash";

var hash = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(toHash);
var hashString = Convert.ToBase64String(hash);
  

Надеюсь, это поможет.

Ответ №3:

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

Одна вещь, которую вы можете сделать, которая работает для этого фида, — это использовать условные запросы HTTP, чтобы попросить сервер предоставить вам данные, только если они действительно были изменены с момента последнего запроса.

Например:

У вас должна быть глобальная переменная / член для хранения даты и времени последнего изменения из вашего фида

     var lastModified = DateTime.MinValue;
  

Затем каждый раз, когда вы делаете запрос, подобный следующему

     var request = (HttpWebRequest)WebRequest.Create( "http://www.extremetech.com/feed" );
    request.IfModifiedSince = lastModified; 
    try {

      using ( var response = (HttpWebResponse)request.GetResponse() ) {

        lastModified  = response.LastModified;

        using ( var stream = response.GetResponseStream() ) {

          //*** parsing the stream
          var reader = XmlReader.Create( stream );
          SyndicationFeed feed = SyndicationFeed.Load( reader );
          }
        }
      }
    catch ( WebException e ) {
      var response = (HttpWebResponse)e.Response;
      if ( response.StatusCode != HttpStatusCode.NotModified )
        throw; // rethrow an unexpected web exception
      }
  

Комментарии:

1. 1 за правильное использование HTTP. Вы также можете использовать заголовок EXPIRES в ответе (если он есть) и метаданные в фиде (дата последнего обновления, период обновления и частота обновления), чтобы указать, когда / как часто вам следует проверять наличие обновлений.