| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- using System.Collections.Generic;
- using System.IO;
- using System.Xml.Schema;
- using System.Xml;
- using System;
- using Microsoft.Extensions.Configuration;
- using Npgsql;
- namespace LAPS_XMLQC_Service.Services
- {
- public class XmlValidatorService
- {
- private readonly string _connectionString;
- public XmlValidatorService(IConfiguration configuration)
- {
- _connectionString = configuration.GetConnectionString("DbConnection");
- }
- public string GetDocumentPath(string documentType, int projectDefinitionId)
- {
- string filePath = string.Empty;
- using (var connection = new NpgsqlConnection(_connectionString))
- {
- connection.Open();
- using (var command = new NpgsqlCommand("SELECT documentpath FROM tblprojectdocuments WHERE documenttype = @documentType AND projectdefinitionid = @projectDefinitionId", connection))
- {
- command.Parameters.AddWithValue("@documentType", documentType);
- command.Parameters.AddWithValue("@projectDefinitionId", projectDefinitionId);
- filePath = Convert.ToString(command.ExecuteScalar());
- }
- }
- return filePath;
- }
- /// <summary>
- /// Validates XML against a DTD file provided via file path.
- /// </summary>
- /// <param name="xmlContent">The XML content as a string.</param>
- /// <param name="dtdFilePath">The file path to the DTD.</param>
- /// <returns>A list of validation errors (empty if valid).</returns>
- /// <summary>
- /// Validates XML content against a specified DTD file.
- /// </summary>
- /// <param name="xmlContent">The XML content as a string.</param>
- /// <param name="dtdFilePath">The path to the DTD file.</param>
- /// <returns>A list of validation errors or a success message if valid.</returns>
- public List<string> ValidateXmlAgainstDtd(string xmlContent, string dtdFilePath)
- {
- var validationErrors = new List<string>();
- try
- {
- // Step 1: Check if the XML is well-formed
- //validationErrors = ValidateXmlAndDetectUnclosedTags(xmlContent);
- //if (validationErrors.Count > 0)
- //{
- // return validationErrors; // Stop further processing if not well-formed
- //}
- // Step 2: Inject the DTD file path if not already present
- string xmlWithDtd = InjectDtdPath(xmlContent, dtdFilePath);
- // Step 3: Set up DTD validation settings
- var settings = new XmlReaderSettings
- {
- DtdProcessing = DtdProcessing.Parse,
- ValidationType = ValidationType.DTD,
- XmlResolver = new XmlUrlResolver()
- };
- // Attach a validation event handler
- settings.ValidationEventHandler += (sender, e) =>
- {
- validationErrors.Add($"DTD validation error: {e.Message}");
- };
- // Step 4: Validate the XML against the DTD
- using (var stringReader = new StringReader(xmlWithDtd))
- using (var xmlReader = XmlReader.Create(stringReader, settings))
- {
- while (xmlReader.Read()) { } // Triggers DTD validation
- }
- // If no errors, indicate successful validation
- if (validationErrors.Count == 0)
- {
- validationErrors.Add("XML is valid against the provided DTD.");
- }
- }
- catch (XmlException ex)
- {
- validationErrors.Add($"XML parsing error: {ex.Message}");
- }
- catch (Exception ex)
- {
- validationErrors.Add($"Unexpected error: {ex.Message}");
- }
- return validationErrors;
- }
- //public List<string> FindUnclosedTags(string xmlString)
- //{
- // var unclosedTags = new List<string>();
- // var stack = new Stack<string>();
- // // Load the XML document
- // XmlDocument xmlDoc = new XmlDocument();
- // xmlDoc.LoadXml(xmlString);
- // // Traverse all elements in the XML document
- // XmlNodeList elements = xmlDoc.GetElementsByTagName("*");
- // foreach (XmlNode element in elements)
- // {
- // // Check for opening tags (elements with children)
- // if (element.NodeType == XmlNodeType.Element)
- // {
- // // If the element is not self-closing, push it onto the stack
- // if (element.HasChildNodes || !IsSelfClosingTag(element))
- // {
- // stack.Push(element.Name);
- // }
- // else
- // {
- // // If it's a self-closing tag, it doesn't need to be tracked
- // // But you could add logic to handle these if needed
- // }
- // }
- // else if (element.NodeType == XmlNodeType.EndElement)
- // {
- // // If it's a closing tag, check if it matches the last element in the stack
- // if (stack.Count > 0 && stack.Peek() == element.Name)
- // {
- // stack.Pop(); // Remove matched opening tag
- // }
- // else
- // {
- // // If there's no match, add the unclosed tag to the list
- // unclosedTags.Add(element.Name);
- // }
- // }
- // }
- // // If any opening tags are left in the stack, they are unclosed
- // while (stack.Count > 0)
- // {
- // unclosedTags.Add(stack.Pop());
- // }
- // return unclosedTags;
- //}
- //// Helper function to determine if an element is self-closing
- //private bool IsSelfClosingTag(XmlNode element)
- //{
- // // Example of simple self-closing tag detection, you may need to refine based on your XML format
- // return element.OuterXml.EndsWith("/>");
- //}
- //public static List<string> ParseXmlWithUnclosedTagDetection(string xmlContent)
- //{
- // var validationErrors = new List<string>();
- // var openElements = new Stack<string>(); // Stack to track open elements
- // var lineNumber = 0;
- // var linePosition = 0;
- // try
- // {
- // using (var reader = new XmlTextReader(xmlContent))
- // {
- // while (reader.Read())
- // {
- // lineNumber = reader.LineNumber;
- // linePosition = reader.LinePosition;
- // // Handle element start
- // if (reader.NodeType == XmlNodeType.Element)
- // {
- // openElements.Push(reader.Name);
- // }
- // // Handle element end
- // else if (reader.NodeType == XmlNodeType.EndElement)
- // {
- // // Check if the closing tag matches the most recent opening tag
- // if (openElements.Count == 0 || openElements.Peek() != reader.Name)
- // {
- // validationErrors.Add($"Unmatched closing tag </{reader.Name}> at line {lineNumber}, position {linePosition}.");
- // }
- // else
- // {
- // openElements.Pop(); // Matched closing tag, pop from stack
- // }
- // }
- // }
- // }
- // // After reading, check for any unclosed tags
- // while (openElements.Count > 0)
- // {
- // var unclosedElement = openElements.Pop();
- // validationErrors.Add($"Unclosed tag <{unclosedElement}> at line {lineNumber}, position {linePosition}.");
- // }
- // if (validationErrors.Count == 0)
- // {
- // validationErrors.Add("XML is well-formed.");
- // }
- // }
- // catch (XmlException ex)
- // {
- // validationErrors.Add($"XML parsing error: {ex.Message} at line {lineNumber}, position {linePosition}.");
- // }
- // catch (Exception ex)
- // {
- // validationErrors.Add($"Unexpected error: {ex.Message}");
- // }
- // return validationErrors;
- //}
- //public static List<string> ValidateXmlAndDetectUnclosedTags(string xmlContent)
- //{
- // var validationErrors = new List<string>();
- // var openElements = new Stack<(string ElementName, int LineNumber, int LinePosition)>(); // Stack to track open elements with line info
- // try
- // {
- // var settings = new XmlReaderSettings
- // {
- // DtdProcessing = DtdProcessing.Ignore, // Disable DTD processing
- // IgnoreComments = true, // Ignore comments
- // IgnoreWhitespace = true // Ignore insignificant whitespace
- // };
- // using (var stringReader = new StringReader(xmlContent))
- // using (var xmlReader = XmlReader.Create(stringReader, settings))
- // {
- // IXmlLineInfo lineInfo = xmlReader as IXmlLineInfo;
- // while (xmlReader.Read())
- // {
- // // Track opening tags and check for closing tags
- // if (xmlReader.NodeType == XmlNodeType.Element)
- // {
- // // Push start element onto the stack
- // openElements.Push((xmlReader.Name, lineInfo?.LineNumber ?? -1, lineInfo?.LinePosition ?? -1));
- // }
- // else if (xmlReader.NodeType == XmlNodeType.EndElement)
- // {
- // if (openElements.Count == 0 || openElements.Peek().ElementName != xmlReader.Name)
- // {
- // // Report unmatched closing tag
- // validationErrors.Add($"Unmatched closing tag </{xmlReader.Name}> at line {lineInfo?.LineNumber}, position {lineInfo?.LinePosition}.");
- // }
- // else
- // {
- // // Matched closing tag, pop the stack
- // openElements.Pop();
- // }
- // }
- // }
- // }
- // // Report unclosed tags remaining in the stack
- // while (openElements.Count > 0)
- // {
- // var (ElementName, LineNumber, LinePosition) = openElements.Pop();
- // validationErrors.Add($"Unclosed tag <{ElementName}> at line {LineNumber}, position {LinePosition}.");
- // }
- // }
- // catch (XmlException ex)
- // {
- // validationErrors.Add($"XML parsing error: {ex.Message} at line {ex.LineNumber}, position {ex.LinePosition}.");
- // }
- // catch (Exception ex)
- // {
- // validationErrors.Add($"Unexpected error: {ex.Message}");
- // }
- // if (validationErrors.Count == 0)
- // {
- // validationErrors.Add("XML is well-formed.");
- // }
- // return validationErrors;
- //}
- //public List<string> ValidateXmlAgainstDtd(string xmlContent, string dtdFilePath)
- //{
- // var validationErrors = new List<string>();
- // try
- // {
- // // Inject the DTD file path into the XML content
- // string xmlWithDtd = string.Empty;
- // if (!xmlContent.Contains("<!DOCTYPE"))
- // {
- // xmlWithDtd = InjectDtdPath(xmlContent, dtdFilePath);
- // }
- // else
- // {
- // xmlWithDtd = xmlContent;
- // }
- // var settings = new XmlReaderSettings
- // {
- // DtdProcessing = DtdProcessing.Parse, // Enable DTD parsing
- // ValidationType = ValidationType.DTD, // Set validation type to DTD
- // XmlResolver = new XmlUrlResolver() // Enable resolution of external DTD files
- // };
- // // Attach a validation event handler to capture errors
- // settings.ValidationEventHandler += (sender, e) =>
- // {
- // validationErrors.Add($"DTD validation error: {e.Message}");
- // };
- // // Create an XmlReader for the XML content with the DTD validation settings
- // using (var stringReader = new StringReader(xmlWithDtd))
- // using (var xmlReader = XmlReader.Create(stringReader, settings))
- // {
- // // Read the XML content (this triggers DTD validation)
- // while (xmlReader.Read()) { }
- // }
- // // If there are no errors, validation passed
- // if (validationErrors.Count == 0)
- // {
- // validationErrors.Add("XML is valid against the provided DTD.");
- // }
- // }
- // catch (XmlException ex)
- // {
- // validationErrors.Add($"XML is not well-formed: {ex.Message}");
- // }
- // catch (XmlSchemaValidationException ex)
- // {
- // validationErrors.Add($"DTD validation error: {ex.Message}");
- // }
- // return validationErrors;
- //}
- /// <summary>
- /// Validates XML against an XSD file provided via file path.
- /// </summary>
- /// <param name="xmlContent">The XML content as a string.</param>
- /// <param name="xsdFilePath">The file path to the XSD.</param>
- /// <returns>A list of validation errors (empty if valid).</returns>
- public List<string> ValidateXmlAgainstXsd(string xmlContent, string xsdFilePath)
- {
- var validationErrors = new List<string>();
- try
- {
- // Load the XSD into an XmlSchemaSet from the file path
- var schemas = new XmlSchemaSet();
- schemas.Add(null, xsdFilePath);
- // Create XmlReaderSettings and attach the validation event handler
- var settings = new XmlReaderSettings
- {
- ValidationType = ValidationType.Schema,
- Schemas = schemas
- };
- // ValidationEventHandler to capture validation errors
- settings.ValidationEventHandler += (sender, e) =>
- {
- validationErrors.Add($"XSD validation error: {e.Message}");
- };
- // Create XmlReader for XML validation
- using (var stringReader = new StringReader(xmlContent))
- using (var xmlReader = XmlReader.Create(stringReader, settings))
- {
- // Read the XML (this will trigger validation)
- while (xmlReader.Read()) { }
- }
- // If there are no errors, validation passed
- if (validationErrors.Count == 0)
- {
- validationErrors.Add("XML is valid against the provided XSD.");
- }
- }
- catch (XmlException ex)
- {
- validationErrors.Add($"XML is not well-formed: {ex.Message}");
- }
- return validationErrors;
- }
- //public List<string> ValidateXmlStructure(string xmlContent)
- //{
- // var errors = new List<string>();
- // try
- // {
- // var settings = new XmlReaderSettings
- // {
- // ConformanceLevel = ConformanceLevel.Document, // Ensures XML is well-formed
- // DtdProcessing = DtdProcessing.Prohibit // Disables DTD processing for security
- // };
- // // Adding a handler for validation events
- // settings.ValidationEventHandler += (sender, e) =>
- // {
- // errors.Add($"Validation Error: {e.Message} at Line {e.Exception.LineNumber}, Position {e.Exception.LinePosition}");
- // };
- // // Using XmlReader to read the XML content
- // using var reader = XmlReader.Create(new StringReader(xmlContent), settings);
- // while (reader.Read()) { } // Reads through the XML document
- // }
- // catch (XmlException ex)
- // {
- // // Captures XML parsing errors like unclosed tags
- // errors.Add($"XML Parsing Error: {ex.Message} at Line {ex.LineNumber}, Position {ex.LinePosition}");
- // }
- // catch (Exception ex)
- // {
- // // Captures other errors
- // errors.Add($"Unexpected Error: {ex.Message}");
- // }
- // return errors; // Return all collected validation errors
- //}
- /// <summary>
- /// Injects a DOCTYPE declaration with a SYSTEM DTD reference into the XML content.
- /// </summary>
- /// <param name="xmlContent">The XML content as a string.</param>
- /// <param name="dtdFilePath">The path to the DTD file.</param>
- /// <returns>The modified XML content with the DOCTYPE declaration.</returns>
- private string InjectDtdPath(string xmlContent, string dtdFilePath, string publicId = null)
- {
- // Check if the DOCTYPE declaration is already present
- if (!xmlContent.Contains("<!DOCTYPE"))
- {
- // Skip over the XML declaration if it exists
- int xmlDeclarationEnd = xmlContent.IndexOf("?>");
- int searchStartIndex = (xmlDeclarationEnd > -1) ? xmlDeclarationEnd + 2 : 0;
- // Locate the root element
- int rootStartIndex = xmlContent.IndexOf('<', searchStartIndex);
- if (rootStartIndex == -1)
- {
- throw new InvalidOperationException("Invalid XML content: No opening tag found.");
- }
- int rootEndIndex = xmlContent.IndexOf('>', rootStartIndex);
- if (rootEndIndex == -1)
- {
- throw new InvalidOperationException("Invalid XML content: No closing tag for the root element.");
- }
- // Extract the root element name (e.g., "catalog")
- string rootElement = xmlContent.Substring(rootStartIndex + 1, rootEndIndex - rootStartIndex - 1).Split(' ')[0];
- // Construct the DOCTYPE declaration
- string doctypeDeclaration = string.IsNullOrEmpty(publicId)
- ? $"<!DOCTYPE {rootElement} SYSTEM \"{dtdFilePath}\">"
- : $"<!DOCTYPE {rootElement} PUBLIC \"{publicId}\" \"{dtdFilePath}\">";
- // Insert the DOCTYPE declaration after the XML declaration (if present)
- if (xmlDeclarationEnd > -1) // XML declaration exists
- {
- return xmlContent.Insert(xmlDeclarationEnd + 2, "\n" + doctypeDeclaration);
- }
- // Otherwise, prepend the DOCTYPE declaration to the XML content
- return doctypeDeclaration + "\n" + xmlContent;
- }
- // If DOCTYPE is already present, return the content unchanged
- return xmlContent;
- }
- /// <summary>
- /// Helper method to inject the DTD file path into the XML's DOCTYPE declaration.
- /// </summary>
- /// <param name="xmlContent">The XML content as a string.</param>
- /// <param name="dtdFilePath">The file path to the DTD.</param>
- /// <returns>The XML content with the DOCTYPE declaration added.</returns>
- //private string InjectDtdPath1(string xmlContent, string dtdFilePath)
- //{
- // const string doctypeDeclaration = "<!DOCTYPE catalog SYSTEM \"{0}\">";
- // if (xmlContent.Contains("<?xml"))
- // {
- // string formattedDoctype = string.Format(doctypeDeclaration, dtdFilePath);
- // // Find the position of the first newline character after the XML declaration
- // int firstNewlineIndex = xmlContent.IndexOf('\n');
- // if (firstNewlineIndex != -1)
- // {
- // // Insert the DOCTYPE declaration after the XML declaration
- // return xmlContent.Insert(firstNewlineIndex + 1, formattedDoctype + "\n");
- // }
- // else
- // {
- // // If no newline is found, assume it's a single line XML and append after the XML declaration
- // return xmlContent + "\n" + formattedDoctype;
- // }
- // }
- // else
- // {
-
- // return $"{string.Format(doctypeDeclaration, dtdFilePath)}\n{xmlContent}";
- // }
- //}
- //public List<string> ValidateXml(string xmlContent, string schemaContent, string schemaType)
- //{
- // var errors = new List<string>();
- // try
- // {
-
- // string xmlWithDtd = $"<!DOCTYPE bookstore SYSTEM \"bookstore.dtd\">\n{xmlContent}";
- // XmlParserContext context = new XmlParserContext(null, null, null, XmlSpace.None);
- // XmlReaderSettings settings = new XmlReaderSettings
- // {
- // // ProhibitDtd = false,
- // DtdProcessing = DtdProcessing.Prohibit,
- // ValidationType = ValidationType.DTD
- // };
- // settings.ValidationEventHandler += (sender, args) =>
- // {
- // errors.Add(args.Message);
- // };
- // using (var reader = XmlReader.Create(new StringReader(xmlWithDtd), settings, context))
- // {
- // while (reader.Read()) { }
- // }
- // }
- // catch (XmlException ex)
- // {
- // errors.Add($"XML Exception: {ex.Message}");
- // }
- // catch (Exception ex)
- // {
- // errors.Add($"General Error: {ex.Message}");
- // }
- // return errors;
- //}
- }
- }
|