SaxParseException в проверке XSD не дает имя элемента

У меня есть xsd-файл и xml-файл, я проверяю xml-файл по xsd-файлу, используя следующий код

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(true);
        factory.setAttribute(
                "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
                "http://www.w3.org/2001/XMLSchema");
        factory.setAttribute(
                "http://java.sun.com/xml/jaxp/properties/schemaSource",
                new InputSource(new StringReader(xsd)));
        Document doc = null;
        try {
            DocumentBuilder parser = factory.newDocumentBuilder();
            MyErrorHandler errorHandler = new MyErrorHandler();
            parser.setErrorHandler(errorHandler);
            doc = parser.parse(new InputSource(new StringReader(xml))); 
            return true;
        } catch (ParserConfigurationException e) {
            System.out.println("Parser not configured: " + e.getMessage());
        } catch (SAXException e) {
            System.out.print("Parsing XML failed due to a "
                    + e.getClass().getName() + ":");
            System.out.println(e.getMessage());
        } catch (IOException e) {
            System.out.println("IOException thrown");
            e.printStackTrace();
        }
        return false;

MyErrorHanlder это

private static class MyErrorHandler implements ErrorHandler {
        public void warning(SAXParseException spe) throws SAXException {
            System.out.println("Warning: " + spe.getMessage() + " getColumnNumber is " + spe.getColumnNumber() + " getLineNumber " + spe.getLineNumber() + " getPublicId " + spe.getPublicId() + " getSystemId " + spe.getSystemId());
        }

        public void error(SAXParseException spe) throws SAXException {
            System.out.println("Error: " + spe.getMessage() + " getColumnNumber is " + spe.getColumnNumber() + " getLineNumber " + spe.getLineNumber() + " getPublicId " + spe.getPublicId() + " getSystemId " + spe.getSystemId());
            throw new SAXException("Error: " + spe.getMessage());
        }

        public void fatalError(SAXParseException spe) throws SAXException {
            System.out.println("Fatal Error:  " + spe.getMessage() + " getColumnNumber is " + spe.getColumnNumber() + " getLineNumber " + spe.getLineNumber() + " getPublicId " + spe.getPublicId() + " getSystemId " + spe.getSystemId());
            throw new SAXException("Fatal Error: " + spe.getMessage());
        }
    }

И когда xml не соответствует xsd, я получаю исключение... но у этого исключения нет имени элемента xsd, из-за которого произошла эта ошибка. Сообщение выглядит так

Сбой синтаксического анализа XML из-за org.xml.sax.SAXException: Ошибка: cvc-minLength-valid: значение '' с length = '0' недопустимо по отношению к фасету относительно minLength '1' для типа 'null'.

Вместо того, чтобы печатать имя элемента xsd, в сообщении об ошибке просто есть ''. Из-за этого я не могу найти и отобразить (пользователю) точный элемент, вызывающий ошибку.

Мой элемент xsd выглядит так

<xs:element name="FullName_FirstName">
    <xs:annotation>
        <xs:appinfo>
            <ie:label>First Name</ie:label>
            <ie:html_element>0</ie:html_element>
        </xs:appinfo>
    </xs:annotation>
    <xs:simpleType>
        <xs:restriction base="xs:string">
            <xs:minLength value="1"/>
        </xs:restriction>
    </xs:simpleType>
</xs:element>

заранее спасибо

1 ответ

Решение

Прежде всего, несколько советов. Вам не нужно создавать документ DOM только для проверки. Это приводит к большим затратам памяти, возможно, даже к исчерпанию больших входных XML-документов. Вы могли бы просто использовать SAXParser, Если вы используете Java 1.5 или более позднюю версию, это даже не обязательно. С этой версии API проверки XML был включен в Java SE. Проверьте пакет javax.xml.validation для получения дополнительной информации. Идея в том, что вы сначала создаете Schema объект, затем получить Validator из того, что может быть использовано для проверки. Он принимает любые Source реализация для ввода. Валидаторы также могут быть предоставлены ErrorHandlers, так что вы можете просто использовать свой класс. Конечно, возможно, что вам действительно понадобится DOM, но в этом случае все же лучше сделать Schema экземпляр и зарегистрируйте это с вашим DocumentBuilderFactory,

Теперь для актуальной проблемы. Это не совсем легко, так как SAXParseException не предоставляет вам много контекстной информации. Лучше всего иметь ContentHandler где-то подключен и отслеживает, в каком элементе вы находитесь, или какую-то другую позиционную информацию. Затем вы можете передать это обработчику ошибок, когда это необходимо. Класс DefaultHandler или же DefaultHandler2 это удобный способ сочетания обработки ошибок и содержимого. Вы найдете эти классы в пакете org.xml.sax.ext.

Я собрал тест, который я опубликую ниже. Теперь я получаю две строки вывода вместо ожидаемой. Если это происходит из-за того, что я использую схему или я не выкидываю исключение и продолжаю обработку, я не уверен. Вторая строка содержит имя элемента, так что этого может быть достаточно. Вы могли бы установить некоторый флаг на ошибки вместо того, чтобы генерировать исключение и завершать анализ.

package jaxb.test;

import java.io.StringReader;
import javax.xml.XMLConstants;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

public class ValidationTest {

    public static void main(String[] args) throws Exception {

        //Test XML and schema
        final String xml = "<?xml version=\"1.0\"?><test><test2></test2></test>";
        final String schemaString =
            "<?xml version=\"1.0\"?>"
            + "<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" elementFormDefault=\"unqualified\" attributeFormDefault=\"unqualified\">"
            + "<xsd:element name=\"test\" type=\"Test\"/>"
            + "<xsd:element name=\"test2\" type=\"Test2\"/>"
            + "<xsd:complexType name=\"Test\">"
            + "<xsd:sequence>"
            + "<xsd:element ref=\"test2\" minOccurs=\"1\" maxOccurs=\"unbounded\"/>"
            + "</xsd:sequence>"
            + "</xsd:complexType>"
            + "<xsd:simpleType name=\"Test2\">"
            + "<xsd:restriction base=\"xsd:string\"><xsd:minLength value=\"1\"/></xsd:restriction>"
            + "</xsd:simpleType>"
            + "</xsd:schema>";

        //Building a Schema instance
        final Source schemaSource =
            new StreamSource(new StringReader(schemaString));
        final Schema schema =
            SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(schemaSource);

        //Creating a SAXParser for our input XML
        //First the factory
        final SAXParserFactory factory = SAXParserFactory.newInstance();
        //Must be namespace aware to receive element names
        factory.setNamespaceAware(true);
        //Setting the Schema for validation
        factory.setSchema(schema);
        //Now the parser itself
        final SAXParser parser = factory.newSAXParser();

        //Creating an instance of our special handler
        final MyContentHandler handler = new MyContentHandler();

        //Parsing
        parser.parse(new InputSource(new StringReader(xml)), handler);

    }

    private static class MyContentHandler extends DefaultHandler {

        private String element = "";

        @Override
        public void startElement(String uri, String localName, String qName,
                Attributes attributes) throws SAXException {

            if(localName != null && !localName.isEmpty())
                element = localName;
            else
                element = qName;

        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
            System.out.println(element + ": " + exception.getMessage());
        }

        @Override
        public void error(SAXParseException exception) throws SAXException {
            System.out.println(element + ": " + exception.getMessage());
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
            System.out.println(element + ": " + exception.getMessage());
        }

        public String getElement() {
            return element;
        }

    }

}

Это немного грубо, но вы можете работать над этим, чтобы получить то, что вам нужно.

Другие вопросы по тегам