示例#1
0
        /// <summary>
        /// Main iTunes parsing method.
        /// </summary>
        /// <param name="parent">Parent stack for current node.</param>
        /// <param name="reader">Current xml feed reader.</param>
        /// <param name="feed">Current feed result.</param>
        /// <param name="root">Flag indicating if parser is the default root parser.</param>
        /// <returns>Flag indicating if current node should be parsed or if next node should be retrieved.</returns>
        public override async Task <bool> Parse(Stack <NodeInformation> parent, XmlReader reader, Feed feed, bool root = true)
        {
            //Init
            bool result;

            //Verify Element Node
            if (result = reader.NodeType == XmlNodeType.Element && (!reader.IsEmptyElement || reader.HasAttributes))
            {
                //Init
                ICommonITunes   target;
                NodeInformation nodeInfo = reader.NodeInformation();

                //Add iTunes to feed content type.
                ICommonFeed feedTarget = feed.CurrentItem ?? (ICommonFeed)feed;
                feedTarget.ContentType |= FeedContentType.ITunes;

                //Set common feed target
                if (feed.CurrentItem != null)
                {
                    feed.CurrentItem.ITunes ??= new ITunesItem();
                    target = feed.CurrentItem.ITunes;
                }
                else
                {
                    feed.ITunes ??= new ITunesFeed();
                    target = feed.ITunes;
                }

                //Identify node name
                switch (reader.LocalName)
                {
                    #region Common

                case "author":     //The group responsible for creating the show/episode.
                {
                    target.Author = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);
                }
                break;

                case "block":     //The podcast/episode show or hide status. (true/false/yes/no)
                {
                    var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    if (bool.TryParse(content, out var blockFlag))
                    {
                        target.Block = blockFlag;
                    }
                    else
                    {
                        target.Block = string.Equals(content, "yes");
                    }
                    break;
                }

                case "explicit":     //The podcast/episode parental advisory information. Explicit language or adult content. (true/false/yes/no)
                {
                    var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    if (bool.TryParse(content, out var explicitFlag))
                    {
                        target.Explicit = explicitFlag;
                    }
                    else
                    {
                        target.Explicit = string.Equals(content, "yes");
                    }
                    break;
                }

                case "image":     //The artwork for the show/episode.
                {
                    var href = reader.GetAttribute("href");
                    if (href != null)
                    {
                        try
                        {
                            //Attempt to parse image URL
                            target.Image = new FeedImage()
                            {
                                Url = new Uri(href)
                            };
                        }
                        catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                        {
                            //Unknown node format
                            SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, href, ex.Message);
                        }
                    }
                    else
                    {
                        //Missing href attribute
                        SetParseError(ParseErrorType.MissingAttribute, nodeInfo, feed, null, "href");
                    }
                    break;
                }

                case "keywords":     //List of words or phrases used when searching.
                {
                    var words = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    target.Keywords = words.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(word => word.Trim()).ToList();
                    break;
                }

                case "title":     //The show/episode title specific for Apple Podcasts.
                {
                    target.Title = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    break;
                }

                case "subtitle":     //Used as the title of the podcast/episode.
                {
                    target.Subtitle = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    break;
                }

                case "summary":     //Description of the podcast/episode.
                {
                    target.Summary = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    break;
                }

                    #endregion Common

                    #region Feed

                case "type":     //The type of show. Episodic (default) / Serial.
                {
                    if (feed.ITunes != null)
                    {
                        var type = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                        switch (type)
                        {
                        case "episodic": feed.ITunes.Type = ITunesType.Episodic; break;

                        case "serial": feed.ITunes.Type = ITunesType.Serial; break;

                        default: SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, type); break;
                        }
                    }
                    else
                    {
                        //Feed ITunes object missing
                        throw new ArgumentNullException("Feed.ITunes");
                    }
                    break;
                }

                case "category":     //The show category information.
                {
                    if (feed.ITunes != null)
                    {
                        //Get category text
                        var category = reader.GetAttribute("text");
                        if (!string.IsNullOrWhiteSpace(category))
                        {
                            //Decode and save category
                            var subCategories = new List <string>();
                            feed.ITunes.Category ??= new Dictionary <string, List <string> >();
                            feed.ITunes.Category.Add(HttpUtility.HtmlDecode(category), subCategories);
                            //Subcategories?
                            if (!reader.IsEmptyElement)
                            {
                                var subTree = reader.ReadSubtree();
                                while (subTree.ReadToFollowing("category", reader.NamespaceURI))
                                {
                                    var subCategory = subTree.GetAttribute("text");
                                    if (subCategory != null)
                                    {
                                        if (!category.Equals(subCategory))
                                        {
                                            subCategories.Add(HttpUtility.HtmlDecode(subCategory));
                                        }
                                    }
                                    else
                                    {
                                        //Missing text attribute
                                        SetParseError(ParseErrorType.MissingAttribute, nodeInfo, feed, null, "text");
                                    }
                                }
                            }
                        }
                        else
                        {
                            //Missing text attribute
                            SetParseError(ParseErrorType.MissingAttribute, nodeInfo, feed, null, "text");
                        }
                    }
                    else
                    {
                        //Feed ITunes object missing
                        throw new ArgumentNullException("Feed.ITunes");
                    }
                    break;
                }

                case "owner":     //The podcast owner contact information. Name and Email address.
                {
                    if (feed.ITunes != null)
                    {
                        //Get owner information
                        var owner         = new ITunesOwner();
                        var ownerElements = await reader.AllSubTreeElements().ConfigureAwait(false);

                        foreach (var element in ownerElements)
                        {
                            switch (element.Key)
                            {
                            case "name": owner.Name = element.Value; break;

                            case "email":
                            {
                                try
                                {
                                    //Attempt to parse owner email
                                    owner.Email = element.Value.ToMailAddress();
                                }
                                catch (Exception ex) when(ex is ArgumentException || ex is ArgumentNullException || ex is FormatException)
                                {
                                    //Unknown node format
                                    SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}, {ex.Message}");
                                }
                                break;
                            }

                            default: SetParseError(ParseErrorType.UnknownSubNode, nodeInfo, feed, element.Value, element.Key); break;
                            }
                        }
                        feed.ITunes.Owner = owner;
                    }
                    else
                    {
                        //Feed ITunes object missing
                        throw new ArgumentNullException("Feed.ITunes");
                    }
                    break;
                }

                case "new-feed-url":     //The new podcast RSS Feed URL.
                {
                    if (feed.ITunes != null)
                    {
                        //Get docs
                        var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                        try
                        {
                            //Attempt to parse new feed URL
                            feed.ITunes.NewFeedUrl = new Uri(content);
                        }
                        catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                        {
                            //Unknown node format
                            SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content, ex.Message);
                        }
                    }
                    else
                    {
                        //Feed ITunes object missing
                        throw new ArgumentNullException("Feed.ITunes");
                    }
                    break;
                }

                    #endregion Feed

                    #region Item

                case "duration":     //The duration of an episode.
                {
                    if (feed.CurrentItem?.ITunes != null)
                    {
                        var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                        if (double.TryParse(content, out var seconds))
                        {
                            //If numeric, assume seconds
                            feed.CurrentItem.ITunes.Duration = TimeSpan.FromSeconds(seconds);
                        }
                        else if (TimeSpan.TryParse(content, out var duration))
                        {
                            //Duration in TimeSpan format
                            feed.CurrentItem.ITunes.Duration = duration;
                        }
                        else
                        {
                            //Unknown node format
                            SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content);
                        }
                    }
                    else
                    {
                        //Feed CurrentItem ITunes object missing
                        throw new ArgumentNullException("Feed.CurrentItem.ITunes");
                    }
                    break;
                }

                case "episodeType":
                {
                    if (feed.CurrentItem?.ITunes != null)
                    {
                        var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                        switch (content)
                        {
                        case "full": feed.CurrentItem.ITunes.EpisodeType = ITunesEpisodeType.Full; break;

                        case "trailer": feed.CurrentItem.ITunes.EpisodeType = ITunesEpisodeType.Trailer; break;

                        case "bonus": feed.CurrentItem.ITunes.EpisodeType = ITunesEpisodeType.Bonus; break;

                        default: SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content); break;
                        }
                    }
                    else
                    {
                        //Feed CurrentItem ITunes object missing
                        throw new ArgumentNullException("Feed.CurrentItem.ITunes");
                    }
                    break;
                }

                case "episode":
                {
                    if (feed.CurrentItem?.ITunes != null)
                    {
                        var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                        if (int.TryParse(content, out var episode))
                        {
                            feed.CurrentItem.ITunes.Episode = episode;
                        }
                        else
                        {
                            //Unknown node format
                            SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content);
                        }
                    }
                    else
                    {
                        //Feed CurrentItem ITunes object missing
                        throw new ArgumentNullException("Feed.CurrentItem.ITunes");
                    }
                    break;
                }

                case "season":
                {
                    if (feed.CurrentItem?.ITunes != null)
                    {
                        var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                        if (int.TryParse(content, out var season))
                        {
                            feed.CurrentItem.ITunes.Season = season;
                        }
                        else
                        {
                            //Unknown node format
                            SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content);
                        }
                    }
                    else
                    {
                        //Feed CurrentItem ITunes object missing
                        throw new ArgumentNullException("Feed.CurrentItem.ITunes");
                    }
                    break;
                }

                    #endregion Item

                default:     //Unknown feed/item node, continue to next.
                {
                    result = false;
                    if (root)
                    {
                        SetParseError(ParseErrorType.UnknownNode, nodeInfo, feed);
                    }
                    break;
                }
                }
            }

            //Return result
            return(result);
        }
        /// <summary>
        /// Main RSS 1.0 parsing method.
        /// </summary>
        /// <param name="parent">Parent stack for current node.</param>
        /// <param name="reader">Current xml feed reader.</param>
        /// <param name="feed">Current feed result.</param>
        /// <param name="root">Flag indicating if parser is the default root parser.</param>
        /// <returns>Flag indicating if current node should be parsed or if next node should be retrieved.</returns>
        /// <remarks>
        /// Because RSS 1.0 almost only overrides older RSS versions, if feed type is NOT RSS 1.0, parsing is handled by base classes.
        /// </remarks>
        public override async Task <bool> Parse(Stack <NodeInformation> parent, XmlReader reader, Feed feed, bool root = true)
        {
            //Init
            bool result;

            //Verify Element Node and feed type
            if (result = reader.NodeType == XmlNodeType.Element && (!reader.IsEmptyElement || reader.HasAttributes) && feed.Type == FeedType.RSS_1_0)
            {
                //Init
                var nodeInfo = reader.NodeInformation();

                //Identify node name
                switch (reader.LocalName)
                {
                    #region Required

                    #region Feed

                case "channel":     //Feed root node. (Override RSS 0.91)
                {
                    //Attempt to get about attribute
                    var about = reader.GetAttribute("rdf:about");
                    if (about != null)
                    {
                        try
                        {
                            //Attempt to parse about URL
                            feed.About = new Uri(about);
                        }
                        catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                        {
                            //Unknown node format
                            SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, about, $"Node: rdf:about, {ex.Message}");
                        }
                    }
                    else
                    {
                        //Missing about attribute
                        SetParseError(ParseErrorType.MissingAttribute, nodeInfo, feed, null, "rdf:about");
                    }
                    break;
                }

                    #endregion Feed

                    #region Item

                case "item":     //Feed item start, add new feed item to feed. (Override RSS 0.91)
                {
                    // Init
                    var item = feed.CurrentItem ?? feed.AddItem();

                    //Attempt to get about attribute
                    var about = reader.GetAttribute("rdf:about");
                    if (about != null)
                    {
                        try
                        {
                            //Attempt to parse about URL
                            item.About = new Uri(about);
                        }
                        catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                        {
                            //Unknown node format
                            SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, about, $"Node: rdf:about, {ex.Message}");
                        }
                    }
                    else
                    {
                        //Missing about attribute
                        SetParseError(ParseErrorType.MissingAttribute, nodeInfo, feed, null, "rdf:about");
                    }
                    break;
                }

                    #endregion Item

                    #endregion Required

                    #region Optional

                    #region Feed

                case "image":     //Specifies a GIF, JPEG or PNG image that can be displayed with the feed. (Override RSS 0.91)
                {
                    //Verify Correct Image Node. If empty, ignore. Otherwise parse.
                    if (!reader.IsEmptyElement)
                    {
                        //Init
                        var image = feed.Image ?? new FeedImage();

                        //Attempt to get about attribute
                        var about = reader.GetAttribute("rdf:about");
                        if (about != null)
                        {
                            try
                            {
                                //Attempt to parse about URL
                                image.About = new Uri(about);
                            }
                            catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, about, $"Node: rdf:about, {ex.Message}");
                            }
                        }
                        else
                        {
                            //Missing about attribute
                            SetParseError(ParseErrorType.MissingAttribute, nodeInfo, feed, null, "rdf:about");
                        }

                        //Get image properties
                        var imageElements = await reader.AllSubTreeElements().ConfigureAwait(false);

                        foreach (var element in imageElements)
                        {
                            switch (element.Key)
                            {
                            case "title": image.Title = element.Value; break;

                            case "description": image.Description = element.Value; break;

                            case "url":
                            {
                                try
                                {
                                    //Attempt to parse image URL
                                    image.Url = new Uri(element.Value);
                                }
                                catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                                {
                                    //Unknown node format
                                    SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}, {ex.Message}");
                                }
                                break;
                            }

                            case "link":
                            {
                                try
                                {
                                    //Attempt to parse link URL
                                    image.Link = new Uri(element.Value);
                                }
                                catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                                {
                                    //Unknown node format
                                    SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}, {ex.Message}");
                                }
                                break;
                            }

                            case "width":
                            {
                                //Attempt to parse width
                                if (int.TryParse(element.Value, out var width))
                                {
                                    image.Width = width;
                                }
                                else
                                {
                                    //Unknown node format
                                    SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}");
                                }
                                break;
                            }

                            case "height":
                            {
                                //Attempt to parse width
                                if (int.TryParse(element.Value, out var height))
                                {
                                    image.Height = height;
                                }
                                else
                                {
                                    //Unknown node format
                                    SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}");
                                }
                                break;
                            }

                            default:
                            {
                                //Unknown node
                                SetParseError(ParseErrorType.UnknownSubNode, nodeInfo, feed, element.Value, element.Key);
                                break;
                            }
                            }
                        }
                        feed.Image = image;
                    }
                    break;
                }

                case "items": break;  //An RDF Sequence is used to contain all the items to denote item order for rendering and reconstruction. (Ignore)

                case "textinput":     //Specifies a text input box that can be displayed with the feed. (Override RSS 0.91)
                {
                    //Verify Correct text input Node. If empty, ignore. Otherwise parse.
                    if (!reader.IsEmptyElement)
                    {
                        //Init
                        var textInput = feed.TextInput ?? new FeedTextInput();

                        //Attempt to get about attribute
                        var about = reader.GetAttribute("rdf:about");
                        if (about != null)
                        {
                            try
                            {
                                //Attempt to parse about URL
                                textInput.About = new Uri(about);
                            }
                            catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, about, $"Node: rdf:about, {ex.Message}");
                            }
                        }
                        else
                        {
                            //Missing about attribute
                            SetParseError(ParseErrorType.MissingAttribute, nodeInfo, feed, null, "rdf:about");
                        }

                        //Get text input properties
                        var textInputElements = await reader.AllSubTreeElements().ConfigureAwait(false);

                        foreach (var element in textInputElements)
                        {
                            switch (element.Key)
                            {
                            case "description": textInput.Description = element.Value; break;

                            case "link":
                            {
                                try
                                {
                                    //Attempt to parse link URL
                                    textInput.Link = new Uri(element.Value);
                                }
                                catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                                {
                                    //Unknown node format
                                    SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}, {ex.Message}");
                                }
                                break;
                            }

                            case "name": textInput.Name = element.Value; break;

                            case "title": textInput.Title = element.Value; break;

                            default: SetParseError(ParseErrorType.UnknownSubNode, nodeInfo, feed, element.Value, element.Key); break;
                            }
                        }
                        feed.TextInput = textInput;
                    }
                    break;
                }

                    #endregion Feed

                    #endregion Optional

                default:     //Unknown feed/item node
                {
                    //Try RSS 0.92 Parse
                    result = await base.Parse(parent, reader, feed, false).ConfigureAwait(false);

                    if (!result && root)
                    {
                        SetParseError(ParseErrorType.UnknownNode, nodeInfo, feed);
                    }
                    break;
                }
                }
            }
            else
            {
                //Try RSS 0.92 Parse
                result = await base.Parse(parent, reader, feed, false).ConfigureAwait(false);
            }

            //Return result
            return(result);
        }
        /// <summary>
        /// Main RSS 0.91 parsing method.
        /// </summary>
        /// <param name="parent">Parent stack for current node.</param>
        /// <param name="reader">Current xml feed reader.</param>
        /// <param name="feed">Current feed result.</param>
        /// <param name="root">Flag indicating if parser is the default root parser.</param>
        /// <returns>Flag indicating if current node should be parsed or if next node should be retrieved.</returns>
        public override async Task <bool> Parse(Stack <NodeInformation> parent, XmlReader reader, Feed feed, bool root = true)
        {
            //Init
            bool result;

            //Verify Element Node
            if (result = reader.NodeType == XmlNodeType.Element && (!reader.IsEmptyElement || reader.HasAttributes))
            {
                //Init
                var nodeInfo = reader.NodeInformation();

                //Set common feed target
                ICommonFeed target = feed.CurrentItem ?? (ICommonFeed)feed;

                //Identify node name
                switch (reader.LocalName)
                {
                    #region Required

                case "channel": break;  //Feed root node. (Ignored)

                case "description":     //Phrase or sentence describing the feed/item.
                {
                    //Get and Set feed/item description
                    var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    target.Description = new FeedText()
                    {
                        Text = content
                    };
                    break;
                }

                case "language":     //The language the feed is written in. (ISO 639)
                {
                    //Get feed language
                    var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    try
                    {
                        //Attempt to set feed Language
                        feed.Language = CultureInfo.GetCultureInfo(content);
                    }
                    catch (Exception ex) when(ex is ArgumentException || ex is CultureNotFoundException)
                    {
                        //Unknown node format
                        SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content, ex.Message);
                    }
                    break;
                }

                case "link":     //The URL to the HTML website corresponding to the feed/item.
                {
                    //Get link
                    var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    try
                    {
                        //Attempt to parse link URL
                        target.Links = new List <FeedLink>()
                        {
                            new FeedLink()
                            {
                                Url = new Uri(content)
                            }
                        };
                    }
                    catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                    {
                        //Unknown node format
                        SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content, ex.Message);
                    }
                    break;
                }

                case "title":     //The name of the feed/item.
                {
                    //Get and Set feed/item title
                    var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    target.Title = new FeedText()
                    {
                        Text = content
                    };
                    break;
                }

                    #endregion Required

                    #region Optional

                    #region Common

                case "pubDate":     //The publication date for the content in the feed/item.
                {
                    //Get publication date
                    var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    //Attemp to parser publication date
                    if (DateTime.TryParse(content, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var pubDate))
                    {
                        target.PubDate = pubDate;
                    }
                    else
                    {
                        //Unknown node format
                        SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content);
                    }
                    break;
                }

                    #endregion Common

                    #region Feed

                case "copyright":     //Copyright notice for content in the feed.
                {
                    //Get and Set feed copyright
                    var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    feed.Copyright = new FeedText()
                    {
                        Text = content
                    };
                    break;
                }

                case "docs":     //A URL that points to the documentation for the format used in the RSS file.
                {
                    //Get docs
                    var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    try
                    {
                        //Attempt to parse docs URL
                        feed.Docs = new Uri(content);
                    }
                    catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                    {
                        //Unknown node format
                        SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content, ex.Message);
                    }
                    break;
                }

                case "image":     //Specifies a GIF, JPEG or PNG image that can be displayed with the feed.
                {
                    //Get image properties
                    var image         = feed.Image ?? new FeedImage();
                    var imageElements = await reader.AllSubTreeElements().ConfigureAwait(false);

                    foreach (var element in imageElements)
                    {
                        switch (element.Key)
                        {
                        case "title": image.Title = element.Value; break;

                        case "description": image.Description = element.Value; break;

                        case "url":
                        {
                            try
                            {
                                //Attempt to parse image URL
                                image.Url = new Uri(element.Value);
                            }
                            catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}, {ex.Message}");
                            }
                            break;
                        }

                        case "link":
                        {
                            try
                            {
                                //Attempt to parse link URL
                                image.Link = new Uri(element.Value);
                            }
                            catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}, {ex.Message}");
                            }
                            break;
                        }

                        case "width":
                        {
                            //Attempt to parse width
                            if (int.TryParse(element.Value, out var width))
                            {
                                image.Width = width;
                            }
                            else
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}");
                            }
                            break;
                        }

                        case "height":
                        {
                            //Attempt to parse height
                            if (int.TryParse(element.Value, out var height))
                            {
                                image.Height = height;
                            }
                            else
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}");
                            }
                            break;
                        }

                        default:
                        {
                            //Unknown node
                            SetParseError(ParseErrorType.UnknownSubNode, nodeInfo, feed, element.Value, element.Key);
                            break;
                        }
                        }
                    }
                    feed.Image = image;
                    break;
                }

                case "lastBuildDate":     //The last time the content of the feed changed.
                {
                    //Init
                    var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    //Attempt to parse last build date
                    if (DateTime.TryParse(content, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var lastBuildDate))
                    {
                        feed.LastBuildDate = lastBuildDate;
                    }
                    else
                    {
                        //Unknown node format
                        SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content);
                    }
                    break;
                }

                case "managingEditor":     //Email address for person responsible for editorial content.
                {
                    //Init
                    var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    try
                    {
                        //Attempt to parse managing editor
                        feed.ManagingEditor = content.ToMailAddress();
                    }
                    catch (Exception ex) when(ex is ArgumentException || ex is ArgumentNullException || ex is FormatException)
                    {
                        //Unknown node format
                        SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content, ex.Message);
                    }
                    break;
                }

                case "rating":     //Protocol for Web Description Resources (POWDER)
                {
                    feed.Rating = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    break;
                }

                case "skipDays":     //Identifies days of the week during which the feed is not updated.
                {
                    //Get skip days
                    var skipDays         = FeedWeekDays.None;
                    var skipDaysElements = await reader.AllSubTreeElements().ConfigureAwait(false);

                    foreach (var element in skipDaysElements)
                    {
                        if (element.Key.Equals("day"))
                        {
                            switch (element.Value)
                            {
                            case "Monday": skipDays |= FeedWeekDays.Monday; break;

                            case "Tuesday": skipDays |= FeedWeekDays.Tuesday; break;

                            case "Wednesday": skipDays |= FeedWeekDays.Wednesday; break;

                            case "Thursday": skipDays |= FeedWeekDays.Thursday; break;

                            case "Friday": skipDays |= FeedWeekDays.Friday; break;

                            case "Saturday": skipDays |= FeedWeekDays.Saturday; break;

                            case "Sunday": skipDays |= FeedWeekDays.Sunday; break;

                            default: SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}"); break;
                            }
                        }
                        else
                        {
                            //Unknown node
                            SetParseError(ParseErrorType.UnknownSubNode, nodeInfo, feed, element.Value, element.Key);
                        }
                    }
                    feed.SkipDays = skipDays;
                    break;
                }

                case "skipHours":     //Identifies the hours of the day during which the feed is not updated.
                {
                    //Get skip hours
                    var skipHours         = new List <int>();
                    var skipHoursElements = await reader.AllSubTreeElements().ConfigureAwait(false);

                    foreach (var element in skipHoursElements)
                    {
                        if (element.Key.Equals("hour"))
                        {
                            if (int.TryParse(element.Value, out var hour))
                            {
                                skipHours.Add(hour);
                            }
                            else
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}");
                            }
                        }
                        else
                        {
                            //Unknown node
                            SetParseError(ParseErrorType.UnknownSubNode, nodeInfo, feed, element.Value, element.Key);
                        }
                    }
                    feed.skipHours = skipHours;
                    break;
                }

                case "textinput":     //Specifies a text input box that can be displayed with the feed.
                {
                    //Get text input properties
                    var textInput         = feed.TextInput ?? new FeedTextInput();
                    var textInputElements = await reader.AllSubTreeElements().ConfigureAwait(false);

                    foreach (var element in textInputElements)
                    {
                        switch (element.Key)
                        {
                        case "description": textInput.Description = element.Value; break;

                        case "link":
                        {
                            try
                            {
                                //Attempt to parse link URL
                                textInput.Link = new Uri(element.Value);
                            }
                            catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}, {ex.Message}");
                            }
                            break;
                        }

                        case "name": textInput.Name = element.Value; break;

                        case "title": textInput.Title = element.Value; break;

                        default: SetParseError(ParseErrorType.UnknownSubNode, nodeInfo, feed, element.Value, element.Key); break;
                        }
                    }
                    feed.TextInput = textInput;
                    break;
                }

                case "webMaster":     //Email address for person responsible for technical issues relating to the feed.
                {
                    //Init
                    var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    try
                    {
                        //Attempt to parse web master
                        feed.WebMaster = content.ToMailAddress();
                    }
                    catch (Exception ex) when(ex is ArgumentException || ex is ArgumentNullException || ex is FormatException)
                    {
                        //Unknown node format
                        SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content, ex.Message);
                    }
                    break;
                }

                case "item":     //Feed item start, add new feed item to feed.
                {
                    //Add new item
                    if (feed.CurrentParseType == ParseType.Feed)
                    {
                        feed.AddItem();
                    }
                    break;
                }

                    #endregion Feed

                    #endregion Optional

                default:     //Unknown feed/item node, if main root parser, log unknown node and continue to next.
                {
                    result = false;
                    if (root)
                    {
                        SetParseError(ParseErrorType.UnknownNode, nodeInfo, feed);
                    }
                    break;
                }
                }
            }
            else if (result = reader.NodeType == XmlNodeType.EndElement)
            {
                switch (reader.LocalName)
                {
                case "item":     //Feed item end, close current feed item.
                {
                    feed.CloseItem();
                    break;
                }
                }
            }

            //Return result
            return(result);
        }
示例#4
0
        /// <summary>
        /// Main Atom parsing method.
        /// </summary>
        /// <param name="parent">Parent stack for current node.</param>
        /// <param name="reader">Current xml feed reader.</param>
        /// <param name="feed">Current feed result.</param>
        /// <param name="root">Flag indicating if parser is the default root parser.</param>
        /// <returns>Flag indicating if current node should be parsed or if next node should be retrieved.</returns>
        public override async Task <bool> Parse(Stack <NodeInformation> parent, XmlReader reader, Feed feed, bool root = true)
        {
            //Init
            bool result;

            //Verify Element Node
            if (result = reader.NodeType == XmlNodeType.Element && (!reader.IsEmptyElement || reader.HasAttributes))
            {
                //Init
                ICommonAtom      target      = feed;
                ICommonAtomFeed? targetFeed  = feed;
                ICommonAtomEntry?targetEntry = feed.CurrentItem;
                NodeInformation  nodeInfo    = reader.NodeInformation();

                //Verify feed type
                if (feed.Type == FeedType.Atom)
                {
                    //Set common target
                    if (feed.CurrentItem != null)
                    {
                        target = feed.CurrentItem;
                    }
                }
                else
                {
                    //Add Atom to feed content type
                    ICommonFeed feedTarget = feed.CurrentItem ?? (ICommonFeed)feed;
                    feedTarget.ContentType |= FeedContentType.Atom;

                    //Set common target
                    if (feed.CurrentItem != null)
                    {
                        feed.CurrentItem.Atom ??= new AtomEntry();
                        target = feed.CurrentItem.Atom;
                    }
                    else
                    {
                        feed.Atom ??= new AtomFeed();
                        target = feed.Atom;
                    }
                    targetFeed  = feed.Atom;
                    targetEntry = feed.CurrentItem?.Atom;
                }

                //Identify node name
                switch (reader.LocalName)
                {
                    #region Common

                case "author":     //Names one author of the feed entry.
                {
                    //Get author properties
                    target.Author = new FeedPerson();
                    var authorElements = await reader.AllSubTreeElements().ConfigureAwait(false);

                    foreach (var element in authorElements)
                    {
                        switch (element.Key)
                        {
                        case "email":
                        {
                            try
                            {
                                //Attempt to parse author email
                                target.Author.Email = element.Value.ToMailAddress();
                            }
                            catch (Exception ex) when(ex is ArgumentException || ex is ArgumentNullException || ex is FormatException)
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}, {ex.Message}");
                            }
                            break;
                        }

                        case "name": target.Author.Name = element.Value; break;

                        case "uri":
                        {
                            try
                            {
                                //Attempt to parse author URI
                                target.Author.Uri = new Uri(element.Value);
                            }
                            catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}, {ex.Message}");
                            }
                            break;
                        }

                        default:
                        {
                            //Unknown node
                            SetParseError(ParseErrorType.UnknownSubNode, nodeInfo, feed, element.Value, element.Key);
                            break;
                        }
                        }
                    }
                    break;
                }

                case "category":     //One or more categories that the feed/entry belongs to.
                {
                    //Verify link node
                    if (reader.HasAttributes && reader.GetAttribute("term") != null)
                    {
                        //Parse and add category to feed/entry catergories list
                        var category = new FeedCategory()
                        {
                            Category = reader.GetAttribute("domain"),
                            Label    = reader.GetAttribute("label")
                        };

                        //Attempt to get category scheme
                        var scheme = reader.GetAttribute("scheme");
                        if (scheme != null)
                        {
                            try
                            {
                                //Attempt to parse category scheme URI
                                category.Scheme = new Uri(scheme);
                            }
                            catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, scheme, $"Node: scheme, {ex.Message}");
                            }
                        }

                        //Add category to categories list
                        target.Categories ??= new List <FeedCategory>();
                        target.Categories.Add(category);
                    }
                    else
                    {
                        //Missing href attribute
                        SetParseError(ParseErrorType.MissingAttribute, nodeInfo, feed, null, "term");
                    }
                    break;
                }

                case "contributor":     //Name of one or more contributors to the feed entry.
                {
                    //Init
                    var contributor = new FeedPerson();

                    //Get contributor properties
                    var contributorElements = await reader.AllSubTreeElements().ConfigureAwait(false);

                    foreach (var element in contributorElements)
                    {
                        switch (element.Key)
                        {
                        case "email":
                        {
                            try
                            {
                                //Attempt to parse contributor email
                                contributor.Email = element.Value.ToMailAddress();
                            }
                            catch (Exception ex) when(ex is ArgumentException || ex is ArgumentNullException || ex is FormatException)
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}, {ex.Message}");
                            }
                            break;
                        }

                        case "name": contributor.Name = element.Value; break;

                        case "uri":
                        {
                            try
                            {
                                //Attempt to parse author URI
                                contributor.Uri = new Uri(element.Value);
                            }
                            catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}, {ex.Message}");
                            }
                            break;
                        }

                        default:
                        {
                            //Unknown node
                            SetParseError(ParseErrorType.UnknownSubNode, nodeInfo, feed, element.Value, element.Key);
                            break;
                        }
                        }
                    }

                    //Add contributor to contributors list
                    target.Contributors ??= new List <FeedPerson>();
                    target.Contributors.Add(contributor);
                    break;
                }

                case "entry":     //Feed entry start, add new feed item to feed.
                {
                    //Add new item
                    if (feed.CurrentParseType == ParseType.Feed)
                    {
                        feed.AddItem();
                    }
                    break;
                }

                case "id":     //Identifies the feed/entry using a universally unique and permanent URI.
                {
                    //Get id
                    var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    try
                    {
                        //Attempt to parse id URI
                        target.Id = new Uri(content);
                    }
                    catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                    {
                        //Unknown node format
                        SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content, ex.Message);
                    }
                    break;
                }

                case "link":     //Link to the referenced resource (typically a Web page)
                {
                    //Verify link node
                    if (reader.HasAttributes && reader.GetAttribute("href") != null)
                    {
                        //Init
                        var link = new FeedLink();

                        //Get link attributes
                        while (reader.MoveToNextAttribute())
                        {
                            //Attempt to parse attribute
                            switch (reader.LocalName)
                            {
                            case "href":
                            {
                                try
                                {
                                    //Attempt to parse link href
                                    link.Url = new Uri(reader.Value);
                                }
                                catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                                {
                                    //Unknown node format
                                    SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, reader.Value, $"Node: {reader.LocalName}, {ex.Message}");
                                }
                                break;
                            }

                            case "hreflang":
                            {
                                try
                                {
                                    //Attempt to parse link hrefLang
                                    link.Language = CultureInfo.GetCultureInfo(reader.Value);
                                }
                                catch (Exception ex) when(ex is ArgumentException || ex is CultureNotFoundException)
                                {
                                    //Unknown node format
                                    SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, reader.Value, $"Node: {reader.LocalName}, {ex.Message}");
                                }
                                break;
                            }

                            case "length":
                            {
                                //Attempt to parse link length
                                if (long.TryParse(reader.Value, out var length))
                                {
                                    link.Length = length;
                                }
                                else
                                {
                                    //Unknown node format
                                    SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, reader.Value, $"Node: {reader.LocalName}");
                                }
                                break;
                            }

                            case "rel":
                            {
                                //Attempt to parse link rel
                                switch (reader.Value)
                                {
                                case "alternate": link.Type = FeedLinkType.Alternate; break;

                                case "enclosure": link.Type = FeedLinkType.Enclosure; break;

                                case "related": link.Type = FeedLinkType.Related; break;

                                case "self": link.Type = FeedLinkType.Self; break;

                                case "via": link.Type = FeedLinkType.Via; break;

                                default: SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, reader.Value, $"Node: {reader.LocalName}"); break;
                                }
                                break;
                            }

                            case "title": link.Text = reader.Value; break;

                            case "type": link.MediaType = reader.Value; break;

                            default:
                            {
                                //Unknown node
                                SetParseError(ParseErrorType.UnknownSubNode, nodeInfo, feed, reader.Value, reader.LocalName);
                                break;
                            }
                            }
                        }

                        //Add link to links collection
                        target.Links ??= new List <FeedLink>();
                        target.Links.Add(link);
                    }
                    else
                    {
                        //Missing href attribute
                        SetParseError(ParseErrorType.MissingAttribute, nodeInfo, feed, null, "href");
                    }
                    break;
                }

                case "rights":     //Conveys information about rights, e.g. copyrights, held in and over the feed.
                {
                    //Attemp to parse rights
                    target.Rights = new FeedText()
                    {
                        Type = reader.GetAttribute("type")
                    };
                    target.Rights.Text = await reader.ReadStartElementAndContentAsStringAsync(target.Rights.Type).ConfigureAwait(false);

                    break;
                }

                case "title":     //The name of the feed/entry.
                {
                    //Attemp to parse title
                    target.Title = new FeedText()
                    {
                        Type = reader.GetAttribute("type")
                    };
                    target.Title.Text = await reader.ReadStartElementAndContentAsStringAsync(target.Title.Type).ConfigureAwait(false);

                    break;
                }

                case "updated":     //Indicates the last time the feed/entry was modified in a significant way.
                {
                    //Init
                    var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                    //Attempt to parse updated date
                    if (DateTime.TryParse(content, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var updated))
                    {
                        target.Updated = updated;
                    }
                    else
                    {
                        //Unknown node format
                        SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content);
                    }
                    break;
                }

                    #endregion Common

                    #region Feed

                case "icon":     //Identifies a small image which provides iconic visual identification for the feed.
                {
                    if (targetFeed != null)
                    {
                        //Get icon
                        var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                        try
                        {
                            //Attempt to parse icon URI
                            targetFeed.Icon = new Uri(content);
                        }
                        catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                        {
                            //Unknown node format
                            SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content, ex.Message);
                        }
                    }
                    else
                    {
                        //Feed Atom object missing
                        throw new ArgumentNullException("Feed.Atom");
                    }
                    break;
                }

                case "generator":     //Indicating the program used to generate the feed.
                {
                    if (targetFeed != null)
                    {
                        //Init
                        targetFeed.Generator = new FeedGenerator();

                        //Attempt to parse optional attributes
                        var uri = reader.GetAttribute("uri");
                        if (uri != null)
                        {
                            try
                            {
                                //Attempt to parse generator uri
                                targetFeed.Generator.Uri = new Uri(uri);
                            }
                            catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, uri, $"Node: uri, {ex.Message}");
                            }
                        }
                        targetFeed.Generator.Version = reader.GetAttribute("version");

                        //Attempt to parse feed generator
                        targetFeed.Generator.Generator = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);
                    }
                    else
                    {
                        //Feed Atom object missing
                        throw new ArgumentNullException("Feed.Atom");
                    }
                    break;
                }

                case "logo":     //Identifies a larger image which provides visual identification for the feed.
                {
                    if (targetFeed != null)
                    {
                        //Init
                        targetFeed.Logo = new FeedImage();

                        //Get logo
                        var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                        try
                        {
                            //Attempt to parse logo URI
                            targetFeed.Logo.Url = new Uri(content);
                        }
                        catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                        {
                            //Unknown node format
                            SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content, ex.Message);
                        }
                    }
                    else
                    {
                        //Feed Atom object missing
                        throw new ArgumentNullException("Feed.Atom");
                    }
                    break;
                }

                case "subtitle":     //Contains a human-readable description or subtitle for the feed.
                {
                    if (targetFeed != null)
                    {
                        //Attemp to parse subtitle
                        targetFeed.Subtitle = new FeedText()
                        {
                            Type = reader.GetAttribute("type")
                        };
                        targetFeed.Subtitle.Text = await reader.ReadStartElementAndContentAsStringAsync(targetFeed.Subtitle.Type).ConfigureAwait(false);
                    }
                    else
                    {
                        //Feed Atom object missing
                        throw new ArgumentNullException("Feed.Atom");
                    }
                    break;
                }

                    #endregion Feed

                    #region Entry

                case "content":     //Contains or links to the complete content of the entry.
                {
                    if (targetEntry != null)
                    {
                        //Attemp to parse content
                        targetEntry.Content = new FeedContent()
                        {
                            Type = reader.GetAttribute("type")
                        };
                        targetEntry.Content.Text = await reader.ReadStartElementAndContentAsStringAsync(targetEntry.Content.Type).ConfigureAwait(false);

                        //Attempt to get content src
                        var src = reader.GetAttribute("src");
                        if (src != null)
                        {
                            try
                            {
                                //Attempt to parse content src
                                targetEntry.Content.Source = new Uri(src);
                            }
                            catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                            {
                                //Unknown node format
                                SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, src, $"Node: src, {ex.Message}");
                            }
                        }
                    }
                    else
                    {
                        if (feed.Type == FeedType.Atom)
                        {
                            //Feed item object missing
                            throw new ArgumentNullException("Feed.CurrentItem");
                        }
                        else
                        {
                            //Feed CurrentItem Atom object missing
                            throw new ArgumentNullException("Feed.CurrentItem.Atom");
                        }
                    }
                    break;
                }

                case "published":
                {
                    if (targetEntry != null)
                    {
                        //Get published
                        var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false);

                        //Attemp to parser published
                        if (DateTime.TryParse(content, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var published))
                        {
                            targetEntry.Published = published;
                        }
                        else
                        {
                            //Unknown node format
                            SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content);
                        }
                    }
                    else
                    {
                        if (feed.Type == FeedType.Atom)
                        {
                            //Feed item object missing
                            throw new ArgumentNullException("Feed.CurrentItem");
                        }
                        else
                        {
                            //Feed CurrentItem Atom object missing
                            throw new ArgumentNullException("Feed.CurrentItem.Atom");
                        }
                    }
                    break;
                }

                case "source":
                {
                    if (targetEntry != null)
                    {
                        //Init
                        targetEntry.Source = new FeedLink();

                        //Get source properties
                        var sourceElements = await reader.AllSubTreeElements().ConfigureAwait(false);

                        foreach (var element in sourceElements)
                        {
                            switch (element.Key)
                            {
                            case "id":
                            {
                                try
                                {
                                    //Attempt to parse id URI
                                    targetEntry.Source.Url = new Uri(element.Value);
                                }
                                catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException)
                                {
                                    //Unknown node format
                                    SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}, {ex.Message}");
                                }
                                break;
                            }

                            case "title":
                            {
                                //Set title text
                                targetEntry.Source.Text = element.Value;
                                break;
                            }

                            case "updated":
                            {
                                //Attempt to parse updated date
                                if (DateTime.TryParse(element.Value, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var updated))
                                {
                                    targetEntry.Source.Updated = updated;
                                }
                                else
                                {
                                    //Unknown node format
                                    SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, element.Value, $"Node: {element.Key}");
                                }
                                break;
                            }

                            default:
                            {
                                //Unknown node
                                SetParseError(ParseErrorType.UnknownSubNode, nodeInfo, feed, element.Value, element.Key);
                                break;
                            }
                            }
                        }
                    }
                    else
                    {
                        if (feed.Type == FeedType.Atom)
                        {
                            //Feed item object missing
                            throw new ArgumentNullException("Feed.CurrentItem");
                        }
                        else
                        {
                            //Feed CurrentItem Atom object missing
                            throw new ArgumentNullException("Feed.CurrentItem.Atom");
                        }
                    }
                    break;
                }

                case "summary":     //Conveys a short summary, abstract, or excerpt of the entry.
                {
                    if (targetEntry != null)
                    {
                        //Attemp to parse summary
                        targetEntry.Summary = new FeedText()
                        {
                            Type = reader.GetAttribute("type")
                        };
                        targetEntry.Summary.Text = await reader.ReadStartElementAndContentAsStringAsync(targetEntry.Summary.Type).ConfigureAwait(false);
                    }
                    else
                    {
                        if (feed.Type == FeedType.Atom)
                        {
                            //Feed item object missing
                            throw new ArgumentNullException("Feed.CurrentItem");
                        }
                        else
                        {
                            //Feed CurrentItem Atom object missing
                            throw new ArgumentNullException("Feed.CurrentItem.Atom");
                        }
                    }
                    break;
                }

                    #endregion Entry

                default:     //Unknown feed/entry node, continue to next.
                {
                    result = false;
                    if (root)
                    {
                        SetParseError(ParseErrorType.UnknownNode, nodeInfo, feed);
                    }
                    break;
                }
                }
            }
            else if (result = reader.NodeType == XmlNodeType.EndElement)
            {
                switch (reader.LocalName)
                {
                case "entry":     //Feed entry end, close current feed item.
                {
                    feed.CloseItem();
                    break;
                }
                }
            }

            //Return result
            return(result);
        }