SaxParseException в проверке XSD не дает имя элемента - PullRequest
2 голосов
/ 18 августа 2011

У меня есть 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 Ответ

5 голосов
/ 19 августа 2011

Прежде всего, несколько советов. Вам не нужно создавать документ 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;
        }

    }

}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...