2016-07-23 63 views
0

我正在創建讀取bbc提要的控制檯應用程序,然後它必須創建帶有新聞對象的json文件。它每小時運行一小時。我的問題是,它是複製父對象,但我不明白爲什麼。奇怪的是,如果你在小時內運行它,它會起作用,但如果你在小時前5分鐘運行它,它會創建這個重複的父元素。Json文件中的重複條目。 C#

public static void Main(string[] args) 
    { 
     // get the starting time of app. 
     DateTime startingTime = DateTime.Now; 
     int minute = 1; 
     int hoursRun = 0; 
     bool folderCreated = false; 
     int n = startingTime.AddHours(hoursRun).Hour; 
     //this will be the folder path for feeds. 
     string feedsFolderPath = Environment.GetFolderPath(
        System.Environment.SpecialFolder.Desktop) + "\\feeds"; 

     // uri for feeds. 
     string bbcURI = "http://feeds.bbci.co.uk/news/uk/rss.xml"; 

     while (true) 
     { 
      // check the hour and if it is more than 1 minutes past the hour wait for the next hour. 
      if (DateTime.Now.Hour == startingTime.AddHours(hoursRun).Hour && DateTime.Now.Minute < minute) 
      { 
       //get feeds 
       News bbcNewsFeed = ProcessFeedHelper.GetFeeds(bbcURI); 

       // if this is the first run go ahead and create a json file. 
       if (hoursRun == 0) 
       { 
        if (!folderCreated) 
        { 
         ProcessFeedHelper.CreateFolder(feedsFolderPath); 
         folderCreated = true; 
        } 
        ProcessFeedHelper.CreateJsonFile(bbcNewsFeed, feedsFolderPath); 

       } 
       else 
       { 
        //if it is the second time then we need to check for duplicates. 
        ProcessFeedHelper.RemoveDuplicatesFeeds(bbcNewsFeed, feedsFolderPath); 
        ProcessFeedHelper.CreateJsonFile(bbcNewsFeed, feedsFolderPath); 
       } 

       // if it is the 23rd hour then we need to reset the counter and detele all files in folder. 
       if (hoursRun == 23) 
       { 
        hoursRun = 0; 

        ProcessFeedHelper.DeleteFilesInDirectory(feedsFolderPath); 
       } 
       else 
       { 

        //others increment the hoursrun. 
        hoursRun++; 
       } 


       bbcNewsFeed = null; 

      } 
     } 
    } 
} 

助手類

public static News GetFeeds(String aURI) 
    { 
     News newsFeed; 

      //instantiate xmlreader and point to uri 
      using (System.Xml.XmlReader reader = System.Xml.XmlReader.Create(aURI)) 
      { 
       //load the feed into SyndicationFeed Object 
       SyndicationFeed feed = SyndicationFeed.Load(reader); 

       newsFeed = new News(); 

       List<NewsItem> newsItemList = new List<NewsItem>(); 


       foreach (var item in feed.Items) 
       { 
        // BBC Feed parent element titles change throughout the day but I have not managed to get them all. 
        // Could potentially break however, the logic is correct. 
        // Here we create the parent element object. 
        if (item.Title.Text == "BBC News Channel" || item.Title.Text == "BBC News at 10") 
        { 

         newsFeed.title = item.Title.Text; 
         newsFeed.link = item.Id; 
         newsFeed.description = item.Summary.Text; 

        } 
        else 
        { 
         NewsItem newsItem = new NewsItem(); 
         newsItem.title = item.Title.Text; 
         newsItem.link = item.Id; 
         newsItem.description = item.Summary.Text; 
         newsItem.publishDate = FormatDate(item.PublishDate.ToString()); 

         //Add it to parent object. 
         newsItemList.Add(newsItem); 
        } 
       } 

       newsFeed.items = newsItemList; 
       //close reader once we have finished reading feed and return feed object. 
       reader.Close(); 

      } 
      return newsFeed; 
    } 
    /// <summary> 
    /// Creates a folder at a specified path. 
    /// </summary> 
    /// <param name="aPath"></param> 
    public static void CreateFolder(string aPath) 
    { 
     System.IO.Directory.CreateDirectory(aPath); 
    } 
    /// <summary> 
    /// Creates a Json formatted file based on a news object passed through. 
    /// </summary> 
    /// <param name="aNews"></param> 
    /// <param name="aPath"></param> 
    public static void CreateJsonFile(News aNews, string aPath) 
    { 

      string filePath = aPath + "\\" + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".json"; 


      //serialises objects in news Object and appends a file. 
      string jsonFile = JsonConvert.SerializeObject(aNews, Newtonsoft.Json.Formatting.Indented); 

      aNews = JsonConvert.DeserializeObject<News>(jsonFile); 

      jsonFile = JsonConvert.SerializeObject(aNews, Newtonsoft.Json.Formatting.Indented); 


      File.AppendAllText(@filePath, jsonFile); 

      Console.WriteLine(jsonFile); 


    } 

    /// <summary> 
    /// Removes Duplicate news articles in new feeds if they are already stored in files. 
    /// </summary> 
    /// <param name="aNews"></param> 
    /// <param name="aPath"></param> 
    public static void RemoveDuplicatesFeeds(News aNews, string aPath) 
    { 
     try 
     { 
      //get paths to all files. 
      string[] filesInDirectory = Directory.GetFiles(aPath); 

      List<News> newsInFiles = new List<News>(); 
      News newsInFile; 

      // loop through files in directory. 
      foreach (string aFile in filesInDirectory) 
      { 
       //Read files file and deserialise the news object putting it in a news collection. 
       StreamReader reader = new StreamReader(aFile); 
       string fileContent = reader.ReadToEnd(); 
       newsInFile = Newtonsoft.Json.JsonConvert.DeserializeObject<News>(fileContent); 

       newsInFiles.Add(newsInFile); 
       reader.Close(); 
      } 
      //only go in here if there is the recent feed has news items. 
      if (aNews.items.Count > 0) 
      { 
       foreach (News aNewsInFile in newsInFiles) 
       { 
        // put news list into new news list so the next loop doesn't crash. 
        List<NewsItem> tempNewsList = new List<NewsItem>(aNews.items); 
        foreach (NewsItem aNewsItemFromCurrentFeed in tempNewsList) 
        { 
         //check that the current news item is not already in files saved. 
         var newsItemAlreadyExists = from nItems in aNewsInFile.items 
                where nItems.title == aNewsItemFromCurrentFeed.title 
                where nItems.publishDate == aNewsItemFromCurrentFeed.publishDate 
                where nItems.link == aNewsItemFromCurrentFeed.link 
                where nItems.description == aNewsItemFromCurrentFeed.description 
                select nItems; 
         // if item already stored in file then we must remove it as we don't want it. 
         if (newsItemAlreadyExists.First() != null) 
         { 
          if (aNews.items.Contains(aNewsItemFromCurrentFeed)) 
          { 
           aNews.items.Remove(aNewsItemFromCurrentFeed); 
          } 
         } 
        } 
       } 
      } 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine("Unexpected Error"); 
     } 
    } 
    /// <summary> 
    /// Deletes all the files in a directory(path specified in parameter). 
    /// </summary> 
    /// <param name="directoryPath"></param> 
    public static void DeleteFilesInDirectory(string directoryPath) 
    { 
     try 
     { 
      //create files collection and directory object. 
      List<FileInfo> importFiles = new List<FileInfo>(); 
      DirectoryInfo tempDirectory = new DirectoryInfo(directoryPath); 

      //get all files in directory. 
      importFiles.AddRange(tempDirectory.GetFiles()); 

      //if the number of files in the directory are greater than zero then delete them. 
      if (importFiles.Count > 0) 
      { 
       for (int i = 0; i < importFiles.Count; i++) 
        importFiles[i].Delete(); 
      } 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine("Unexpected Error"); 
     } 
    } 
    /// <summary> 
    /// Formats a string to ddd, mm yyyy hh:ss gmt 
    /// </summary> 
    /// <param name="aDate"></param> 
    /// <returns></returns> 
    private static String FormatDate(String aDate) 
    { 
     try 
     { 
      //split string 
      char[] delimiters = { ' ', ',', ':', '/' }; 
      string[] tokens = aDate.Split(delimiters); 
      int year = int.Parse(tokens[2]); 
      int month = int.Parse(tokens[1]); 
      int day = int.Parse(tokens[0]); 
      int hh = int.Parse(tokens[3]); 
      int mm = int.Parse(tokens[4]); 
      int ss = int.Parse(tokens[5]); 

      //create date time object. and add gmt to end of string. 
      DateTime date = new DateTime(year, month, day, hh, mm, ss); 
      return date.ToUniversalTime().ToString("r"); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine("Unexpected Error"); 
     } 
     return ""; 
    } 
} 

父類

class News 
{ 
    public string title { get; set; } 
    public string link{ get; set; } 
    public string description{ get; set; }   
    public IList<NewsItem> items{ get; set; } 


} 

子類

class NewsItem 
{ 
    public string title { get; set; } 
    public string description { get; set; } 
    public string link { get; set; } 
    public string publishDate { get; set; } 

} 

文件示例(不要以爲有端部)

{ 
    "title": "BBC News Channel", 
    "link": "http://www.bbc.co.uk/news/10318089", 
    "description": "Britain's most-watched news channel, delivering breaking  news and analysis all day, every day.", 
    "items": [ 
    { 
     "title": "Dover ferry port chaos leads to 14-hour traffic jams", 
     "description": "Delays at the Port of Dover have caused up to 14-hour tailbacks on the A20 /M20 with Kent Police warning disruption could last for another two days.", 
     "link": "http://www.bbc.co.uk/news/uk-england-kent-36873632", 
     "publishDate": "Sat, 23 Jul 2016 19:38:36 GMT" 
    }, ] 
} { 
    "title": "BBC News Channel", 
    "link": "http://www.bbc.co.uk/news/10318089", 
    "description": "Britain's most-watched news channel, delivering breaking news and analysis all day, every day.", 
    "items": [] 
} 
+1

如果你運行它前5分鐘小時,我希望它在23個小時內什麼也不做......我的理解錯了嗎?如果你在8點55分開始,我會認爲你的情況是(僞代碼)「如果小時== 8分鐘== 0」,這將不會發生另外23小時5分鐘。 – smarx

回答

1

我認爲這個問題可能競爭條件在這裏:

if (DateTime.Now.Hour == startingTime.AddHours(hoursRun).Hour && DateTime.Now.Minute < minute) 

假設你在8:59開始的節目,所以我在我上面的評論中指出,它的尋找小時爲8,分鐘爲0.你會認爲這不會發生23小時左右,但...

想象一下,在8:59:59.9999,這種情況檢查DateTime.Now.Hour == startingTime.AddHours(hoursRun).Hour並返回true,因爲小時當前是8.因此執行繼續檢查下一個條件:DateTime.Now.Minute < minute。時間已過,因此在檢查條件時已是9點。所以這兩個條件都是真的,並且代碼被執行。 (時生成的文件名爲2016-07-23-09.json

現在hoursRun遞增,所以它現在是循環的9

下一個迭代的時間是一樣的東西9時00分05秒。兩個條件都是真的(小時爲9,分鐘爲0),所以代碼再次運行,附加到同一個文件(2016-07-23-09.json)。

如果我的預感是正確的,可能是最小的解決將是做到這一點,這使得務必檢查的同時小時和分鐘部分:

while (true) 
{ 
    var now = DateTime.Now; 
    if (now.Hour == startingTime.AddHours(hoursRun).Hour && now.Minute < minute) 
    { 

我還建議把睡眠聲明while循環...你可能在這個緊密的繁忙循環中燒了大量的CPU。

編輯

哦也,你可能並不意味着等待23小時擺在首位。 :-)你可以在任何地方使用一個+1作爲一個簡單的修復(雖然這意味着如果你在8:00運行程序,它將等到9:00寫第一個文件)。

EDIT 2

如果你不關心運行「的時刻,」這可能是構建循環更簡單的方法:

DateTime lastRun = DateTime.MinValue; 

while (true) 
{ 
    // sleep for 10 minutes at a time until an hour has passed 
    while ((DateTime.UtcNow - lastRun) < TimeSpan.FromHours(1)) 
    { 
     Thread.Sleep(TimeSpan.FromMinutes(10)); 
    } 

    // do work in here 

    // remember the last time we did work 
    lastRun = DateTime.UtcNow; 
} 
+0

謝謝,經過今天上午的測試後,情況就是如此。我自己有點短視的解決方案。但是,系列化是更大的問題。我已經決定創建文件部分到目前爲止似乎沒有發生的主類中。我認爲這是因爲它爲方法堆棧創建了一個本地副本,並且仍在查看堆。然後它將兩個序列化。我知道這可能會很好,但它可以幫助我休息。 – spaga