Validating XML on XSD with the error line numbers

前端 未结 6 1030
盖世英雄少女心
盖世英雄少女心 2021-01-04 15:19

Is there any way to validate an XML file on an XSD schema with the output of the error line numbers?

The XmlReader reader doesn\'t allow line numbers, it shows only

相关标签:
6条回答
  • 2021-01-04 15:33

    Woah, rather tricky this XSD stuff, I'm also new :)

    As it was said before the position information gets lost in the XmlDocument.

    I got it finally running using the XmlReader in combination with XmlDocument:

    // xmlStream and xsdStream are open streams that 
    // point to the respective xml and xsd files
    public void ReadAndVerify(Stream xmlStream, Stream xsdStream)
    {
        // Read the scheme validation and compile it
        XmlSchemaSet schemaSet = new XmlSchemaSet();
    
        using (XmlReader r = XmlReader.Create(xsdStream)) 
        {
            schemaSet.Add(XmlSchema.Read(r, null));
        }
    
        schemaSet.CompilationSettings = new XmlSchemaCompilationSettings();
        schemaSet.Compile();
    
        // Setup the settings for the reader.
        // This includes the previously compiled schema
        XmlReaderSettings settings = new XmlReaderSettings();
        settings.CloseInput = true;
    
        // This is the callback method see below
        settings.ValidationEventHandler += ValidationEventHandler;
    
        settings.ValidationType = ValidationType.Schema;
        settings.Schemas = schemaSet;  // <-- here the schema is set
    
        // To be honest, this is cut'n'paste. Not sure which flags are really required.
        settings.ValidationFlags =
                XmlSchemaValidationFlags.ReportValidationWarnings |
        XmlSchemaValidationFlags.ProcessIdentityConstraints |
        XmlSchemaValidationFlags.ProcessInlineSchema |
        XmlSchemaValidationFlags.ProcessSchemaLocation;
    
        // Now the validating reader is created
        using (XmlReader validatingReader = XmlReader.Create(xmlStream, settings)) 
        {
    
            // This has to be done BEFORE the validating while loop
            XmlDocument x = new XmlDocument();
            x.Load(validatingReader);
    
            // This is the validation loop
            while (validatingReader.Read()) ;
    
            // This is the client code that actually uses the XmlDocument nodes.
            XmlNode node = x[RootNode];
            ReadAllParameters(node);
        }
    }
    

    And the ValidationEventHandler:

    private void ValidationEventHandler(object sender, ValidationEventArgs e)
    {
        // This actually works with this approach
        string text = $"[Line: {e.Exception?.LineNumber}, Column: {e.Exception?.LinePosition}]: {e.Message}";
        switch (e.Severity) {
            case XmlSeverityType.Error:
                Logger.Error(text);
                break;
            case XmlSeverityType.Warning:
                Logger.Warn(e.Message);
                break;
        }
    }
    

    Life can be so simple ;-)

    0 讨论(0)
  • 2021-01-04 15:40

    See the original MSDN example:

    http://msdn.microsoft.com/en-us/library/ms172454.aspx

    ValidationEventHandler's ValidationEventArgs argument has Exception.LineNumber:

    private void SchemaValidationEventHandler(object sender, ValidationEventArgs e) {
        Console.WriteLine("XML {0}: {1} (Line {2})",
                             e.Severity,
                             e.Message,
                             e.Exception.LineNumber);
    }
    
    0 讨论(0)
  • 2021-01-04 15:47

    This example is the most complete I've found. It's slower than other validation methods, but it's the best for debugging: It writes validated element for each line and errors found.

    To have numbers of lines with errors just add this line in ValidationHandler method:

    Console.WriteLine(args.Exception.LineNumber);
    

    Pay attention that xsd file is specified in the root tag of the Xml doc.

    0 讨论(0)
  • 2021-01-04 16:00

    Since @chris-watts suggested to post my comment again as answer. Here it is.

    The document only has line number information if it was loaded with the appropriate flags:

    var opts = LoadOptions.PreserveWhitespace 
             | LoadOptions.SetLineInfo;
    XDocument doc = XDocument.Load(fileStream, opts); 
    
    0 讨论(0)
  • 2021-01-04 16:00

    You can use XMLStarlet. That supports validating, and provides error line numbers:

    $ xml val -e --xsd file.xsd file.xml
    file.xml:8: Element 'atitle': This element is not expected. Expected is ( title ).
    file.xml - invalid
    
    0 讨论(0)
  • 2021-01-04 16:00

    Try this working example. IXmlLineInfo gets you the line info. I am getting all the list of errors here, concatenating the Line number, unique id of that specific record, the element name where the error occurred and adding it to a list.

           //get the input file here - You can replace this to your local file
            var httpRequest = HttpContext.Current.Request;
    
            if (httpRequest.Files.Count > 0)
            {
                var postedFile = httpRequest.Files[0];
    
                //sete the xsd schema path                    
                string xsdPath = HttpContext.Current.Server.MapPath("~/XSD/MyFile.xsd");
    
                //set the XSD schema here
                var schema = new XmlSchemaSet();
                schema.Add("", xsdPath);
                var Message = "";
    
                //validate the xml schema here
                XDocument document = XDocument.Load(postedFile.InputStream, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo | LoadOptions.SetBaseUri);
                //create a lists to add the error records
                List<string> lstErrors = new List<string>();
                document.Validate(schema, ValidationEventHandler);
    
                //validate all the errors
                document.Validate(schema, (sender, args) =>
                 {
                     IXmlLineInfo item = sender as IXmlLineInfo;
                     if (item != null && item.HasLineInfo())
                     {
                         //capture all the details needed here seperated by colons
                         Message = item.LineNumber + ";" +
                         (((System.Xml.Linq.XObject)item).Parent.Element("id")).Value + ";" +
                         ((System.Xml.Linq.XElement)item).Name.LocalName + ";" +
                         args.Message + Environment.NewLine;
                         //add the error to a list
                         lstErrors.Add(Message);
                     }
                 });
            }
    
    0 讨论(0)
提交回复
热议问题