/// <summary> /// Create and Add Parse error to the current feed result. /// </summary> /// <param name="errorType">Type of parse error.</param> /// <param name="nodeInformation">Current node base information.</param> /// <param name="feed">Current feed result.</param> /// <param name="content">Current node content data. (Optonal)</param> /// <param name="message">Parse error message. (Optional)</param> protected void SetParseError(ParseErrorType errorType, NodeInformation nodeInformation, Feed feed, string?content = null, string?message = null) { //Create and Add Parse error to the current feed result var error = ParseError.Create(nodeInformation, ToString(), errorType, feed.CurrentParseType, content, message); (feed.ParseError ??= new List <ParseError>()).Add(error); Debug.WriteLine(error); }
/// <summary> /// Main parse feed stream method. /// </summary> /// <param name="source">Feed source name/url.</param> /// <param name="stream">Feed stream.</param> /// <param name="cancellationToken">Parse task cancellation token.</param> /// <returns>The parsed feed result Feed object.</returns> public static async Task <Feed> ParseFeedAsync(string source, Stream stream, CancellationToken cancellationToken) { //Init bool parseNode; var feed = new Feed(); var defaultParser = RSS_2_0_Parser.Instance; var settings = new XmlReaderSettings() { CloseInput = true, ConformanceLevel = ConformanceLevel.Document, IgnoreWhitespace = true, IgnoreComments = true, Async = true, DtdProcessing = DtdProcessing.Ignore }; using var reader = XmlReader.Create(stream, settings); try { //Start Reading and move to root node if (parseNode = await reader.MoveToContentAsync().ConfigureAwait(false) != XmlNodeType.None) { //Save Parent Node var parent = new Stack <NodeInformation>(new List <NodeInformation>() { reader.NodeInformation() }); //Attempt to identify feed type defaultParser = ParseFeedType(reader, feed) ?? defaultParser; //Read first feed node parseNode = await reader.ReadAsync().ConfigureAwait(false); //Identify and parse nodes until EOF or Cancellation while (parseNode && !reader.EOF && !cancellationToken.IsCancellationRequested) { //Feed node or Extended Namespace node? if (string.IsNullOrWhiteSpace(reader.NamespaceURI)) { //Parse node with default feed parser await defaultParser.Value.Parse(parent, reader, feed).ConfigureAwait(false); } else if (parseNode = parsers.TryGetValue(reader.NamespaceURI, out var parser)) { //Parse node with current Namespace parser await parser.Value.Parse(parent, reader, feed).ConfigureAwait(false); } else { //Unknown Namespace var error = ParseError.Create(reader.NodeInformation(), "FeedParser", ParseErrorType.UnknownNamespace, feed.CurrentParseType); (feed.ParseError ??= new List <ParseError>()).Add(error); Debug.WriteLine(error); } //Save Parent Node if (reader.NodeType != XmlNodeType.EndElement && reader.Depth > (parent.Count > 0 ? parent.Peek().Depth : -1)) { parent.Push(reader.NodeInformation()); } //Read next node parseNode = await reader.ReadAsync().ConfigureAwait(false); //Verify Parent while (parent.Count > 0 && reader.Depth <= parent.Peek().Depth) { parent.Pop(); } } } } //Rethrow Feed Parser Exception catch (FeedParseException ex) { throw ex; } catch (Exception ex) { //Rethrow all other exceptions as FeedParseException throw new FeedParseException(source, reader, ex); } finally { //Close Reader and Stream reader?.Close(); } //Return Parsed Feed return(feed); }