/// <summary> /// Main Content 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 NodeInformation nodeInfo = reader.NodeInformation(); //Add Content to feed content type. ICommonFeed feedTarget = feed.CurrentItem ?? (ICommonFeed)feed; feedTarget.ContentType |= FeedContentType.Content; //Set common feed target ICommonContent target = feed.CurrentItem ?? (ICommonContent)feed; //Identify node name switch (reader.LocalName) { #region Common case "encoded": //Contains the complete encoded content of the feed/item. { //Attemp to get encoded content target.Content = new FeedContent() { Type = "text/html" }; target.Content.Text = await reader.ReadStartElementAndContentAsStringAsync(target.Content.Type).ConfigureAwait(false); break; } #endregion Common 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 0.92 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 Optional #region Common case "category": //One or more categories that the feed/item belongs to. { //Parse and add category to feed/item catergories list target.Categories ??= new List <FeedCategory>(); var domain = reader.GetAttribute("domain"); var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false); target.Categories.Add(new FeedCategory() { Domain = domain, Category = content }); break; } #endregion Common #region Feed case "cloud": //Allows processes to register with a cloud to be notified of updates to the feed, implementing a lightweight publish-subscribe protocol for RSS feeds. { //Parse Cloud attributes feed.Cloud = new FeedCloud() { Domain = reader.GetAttribute("domain"), Path = reader.GetAttribute("path"), Port = reader.GetAttribute("port"), Protocol = reader.GetAttribute("protocol"), RegisterProcedure = reader.GetAttribute("registerProcedure") }; break; } #endregion Feed #region Item case "enclosure": //Media object that is attached to the feed item. { if (feed.CurrentItem != null) { //Attempt to parse enclosure var content = new MediaContent() { Type = reader.GetAttribute("type") }; //Attempt to parse length if (long.TryParse(reader.GetAttribute("length"), out var length)) { content.FileSize = length; } //Get enclosure url var url = reader.GetAttribute("url"); try { //Attempt to parse enclosure URL content.Url = new Uri(url); } catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException) { //Unknown node format SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, url, $"Node: url, {ex.Message}"); } //Set item Enclosure feed.CurrentItem.Enclosure = content; } else { //Feed item object missing throw new ArgumentNullException("Feed.CurrentItem"); } break; } case "source": //The feed that the feed item came from. { if (feed.CurrentItem != null) { //Init var url = reader.GetAttribute("url"); var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false); //Attempt to parse source feed.CurrentItem.Source = new FeedLink() { Type = FeedLinkType.Via, Text = content }; try { //Attempt to parse enclosure URL feed.CurrentItem.Source.Url = new Uri(url); } catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException) { //Unknown node format SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, url, $"Node: url, {ex.Message}"); } } else { //Feed item object missing throw new ArgumentNullException("Feed.CurrentItem"); } break; } #endregion Item #endregion Optional default: //Unknown feed/item node { //Try RSS 0.91 Parse result = await base.Parse(parent, reader, feed, false).ConfigureAwait(false); if (!result && root) { SetParseError(ParseErrorType.UnknownNode, nodeInfo, feed); } break; } } } else { //Try RSS 0.91 Parse result = await base.Parse(parent, reader, feed, false).ConfigureAwait(false); } //Return result return(result); }
/// <summary> /// Main Rdf 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 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(); var parentName = parent.Count > 0 ? parent.Peek().LocalName : string.Empty; //Add Rdf to feed content type. ICommonFeed feedTarget = feed.CurrentItem ?? (ICommonFeed)feed; feedTarget.ContentType |= FeedContentType.Rdf; //Identify node name switch (reader.LocalName) { case "Seq": { switch (parentName) { case "items": //An RDF Sequence is used to contain all the items to denote item order for rendering and reconstruction. { var itemsSequence = new List <Uri>(); if (!reader.IsEmptyElement) { var subTree = reader.ReadSubtree(); while (subTree.ReadToFollowing("li", reader.NamespaceURI)) { var resource = subTree.GetAttribute("resource"); if (resource != null) { try { //Attempt to parse resource URL itemsSequence.Add(new Uri(resource)); } catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException) { //Unknown node format SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, resource, ex.Message); } } else { //Missing resource attribute SetParseError(ParseErrorType.MissingAttribute, nodeInfo, feed, null, "resource"); } } } feed.itemsSequence = itemsSequence; break; } default: result = false; break; } break; } default: //Unknown feed/item node, continue to next. { result = false; if (root) { SetParseError(ParseErrorType.UnknownNode, nodeInfo, feed); } break; } } } //Return result return(Task.FromResult(result)); }
/// <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 2.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> 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 NodeInformation nodeInfo = reader.NodeInformation(); //Set common feed target ICommonFeed target = feed.CurrentItem ?? (ICommonFeed)feed; //Identify node name switch (reader.LocalName) { #region Optional #region Feed case "generator": //A string indicating the program used to generate the feed. { //Attempt to parse feed generator feed.Generator ??= new FeedGenerator(); feed.Generator.Generator = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false); break; } case "ttl": //Number of minutes that indicates how long a feed can be cached before refreshing from the source. { //Attempt to parse feed time to live var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false); if (double.TryParse(content, out var ttl)) { feed.Ttl = TimeSpan.FromMinutes(ttl); } break; } #endregion Feed #region Item case "author": //Email address of the author of the feed item. { if (feed.CurrentItem != null) { //Init var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false); try { //Attempt to parse feed item author feed.CurrentItem.Author ??= new FeedPerson(); feed.CurrentItem.Author.Email = 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); } } else { //Feed item object missing throw new ArgumentNullException("Feed.CurrentItem"); } break; } case "comments": //URL of a page for comments relating to the feed item. { if (feed.CurrentItem != null) { //Init var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false); try { //Attempt to parse feed item comments URL feed.CurrentItem.Comments = 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 item object missing throw new ArgumentNullException("Feed.CurrentItem"); } break; } case "guid": //A string/link that uniquely identifies the feed item. { if (feed.CurrentItem != null) { //Attempt to parse feed item guid var isPermaLink = reader.GetAttribute("isPermaLink"); var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false); feed.CurrentItem.Guid = new FeedGuid() { Guid = content }; if (bool.TryParse(isPermaLink, out var isLink)) { feed.CurrentItem.Guid.IsPermaLink = isLink; if (isLink) { try { //Attempt to parse feed item guid as URL feed.CurrentItem.Guid.Link = new Uri(content); } catch (Exception ex) when(ex is ArgumentNullException || ex is UriFormatException) { //Unknown node format SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content, $"Node: Link, {ex.Message}"); } } } } else { //Feed item object missing throw new ArgumentNullException("Feed.CurrentItem"); } break; } #endregion Item #endregion Optional default: //Unknown feed/item node { //Try RSS 1.0 Parse result = await base.Parse(parent, reader, feed, false).ConfigureAwait(false); if (!result && root) { SetParseError(ParseErrorType.UnknownNode, nodeInfo, feed); } break; } } } else { //Try RSS 1.0 Parse result = await base.Parse(parent, reader, feed, false).ConfigureAwait(false); } //Return result return(result); }
/// <summary> /// Main Spotify 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(); feed.Spotify ??= new SpotifyFeed(); //Add Spotify to feed content type. ICommonFeed feedTarget = feed.CurrentItem ?? (ICommonFeed)feed; feedTarget.ContentType |= FeedContentType.Spotify; //Identify node name switch (reader.LocalName) { #region Feed case "limit": //Number of concurrent episodes (items) to display. { if (feed.Spotify != null) { //Get limit count var content = reader.GetAttribute("recentCount"); if (content != null) { if (int.TryParse(content, out var count)) { feed.Spotify.Limit = count; } else { //Unknown node format SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content); } } else { //Missing count attribute SetParseError(ParseErrorType.MissingAttribute, nodeInfo, feed, null, "recentCount"); } } else { //Feed Spotify object missing throw new ArgumentNullException("Feed.Spotify"); } } break; case "countryOfOrigin": //Defines the intended market/territory ranked in order of priority where the podcast is relevant to the consumer. (List of ISO 3166 codes). { if (feed.Spotify != null) { //Get Countries of origin var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false); try { //Attempt to set country of origin list feed.Spotify.countryOfOrigin = content.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(country => new RegionInfo(country)).ToList(); } catch (Exception ex) when(ex is ArgumentNullException || ex is ArgumentException) { //Unknown node format SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content, ex.Message); } } else { //Feed Spotify object missing throw new ArgumentNullException("Feed.Spotify"); } break; } #endregion Feed 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 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); }
/// <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); }
/// <summary> /// Main Geo RSS 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 GeoInformation?target = null; var nodeInfo = reader.NodeInformation(); //Add Geo RSS to feed content type. ICommonFeed feedTarget = feed.CurrentItem ?? (ICommonFeed)feed; feedTarget.ContentType |= FeedContentType.GeoRSS; //Media RSS Parent? var mediaParent = parent.FirstOrDefault(ancestor => FeedParser.ContentTypeNamespace[FeedContentType.MediaRSS].Contains(ancestor.Namespace)); if (mediaParent != null) { switch (mediaParent.LocalName) { case "location": { if (feed.CurrentItem != null) { target = feed.CurrentItem.Media?.CurrentInformation?.Locations?.LastOrDefault()?.GeoInformation ?? new GeoInformation(); } else { target = feed.MediaInformation?.Locations?.LastOrDefault()?.GeoInformation ?? new GeoInformation(); } break; } } } //Set feed/feed item target if (target == null) { if (feed.CurrentItem != null) { //Get/Set feed item geographical information target = feed.CurrentItem.GeoInformation ??= new GeoInformation(); } else { //Get/Set feed geographical information target = feed.GeoInformation ??= new GeoInformation(); } } //Identify node name switch (reader.LocalName) { case "box": //A bounding box is a rectangular region, often used to define the extents of a map or a rough area of interest. case "lowerCorner": case "upperCorner": { //Set target box coordinates await SetCoordinates(GeoType.Box, target, reader, feed, nodeInfo).ConfigureAwait(false); break; } case "elev": { //Init var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false); //Attempt to parse elevation. if (int.TryParse(content, out var elevation)) { target.Elevation = elevation; } else { //Unknown node format SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content); } break; } case "featurename": //Feauture name of the geographical information. { //Attempt to parse feature name. target.FeatureName = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false); break; } case "featuretypetag": //Feauture type of the geographical information. { //Attempt to parse feature type. target.FeatureType = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false); break; } case "floor": { //Init var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false); //Attempt to parse floor. if (int.TryParse(content, out var floor)) { target.Floor = floor; } else { //Unknown node format SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content); } break; } case "line": //A line contains a space seperated list of latitude-longitude pairs, with each pair separated by whitespace. { //Set target line coordinates await SetCoordinates(GeoType.Line, target, reader, feed, nodeInfo).ConfigureAwait(false); break; } case "point": //A point contains a single latitude-longitude pair, separated by whitespace. case "pos": { //Set target point coordinates await SetCoordinates(GeoType.Point, target, reader, feed, nodeInfo).ConfigureAwait(false); break; } case "posList": { switch (parent.Peek().LocalName) { case "LinearRing": //A polygon contains a space seperated list of latitude-longitude pairs, with each pair separated by whitespace. { //Set target polygon coordinates await SetCoordinates(GeoType.Polygon, target, reader, feed, nodeInfo).ConfigureAwait(false); break; } case "LineString": //A line contains a space seperated list of latitude-longitude pairs, with each pair separated by whitespace. { //Set target line coordinates await SetCoordinates(GeoType.Line, target, reader, feed, nodeInfo).ConfigureAwait(false); break; } } break; } case "polygon": //A polygon contains a space seperated list of latitude-longitude pairs, with each pair separated by whitespace. { //Set target polygon coordinates await SetCoordinates(GeoType.Polygon, target, reader, feed, nodeInfo).ConfigureAwait(false); break; } case "radius": { //Init var content = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false); //Attempt to parse radius. if (int.TryParse(content, out var radius)) { target.Radius = radius; } else { //Unknown node format SetParseError(ParseErrorType.UnknownNodeFormat, nodeInfo, feed, content); } break; } case "relationshiptag": //Relationship of the geographical information. { //Attempt to parse relationship. target.Relationship = await reader.ReadStartElementAndContentAsStringAsync().ConfigureAwait(false); break; } case "Envelope": case "exterior": case "LinearRing": case "LineString": case "Point": case "where": break; default: //Unknown feed/item node, continue to next. { result = false; if (root) { SetParseError(ParseErrorType.UnknownNode, nodeInfo, feed); } break; } } } //Return result return(result); }