Как лучше всего проверить XML-файл на соответствие XSD-файлу?

Я создаю некоторые файлы xml, которые должны соответствовать предоставленному мне файлу xsd. Как лучше всего проверить соответствие?


person Jeff    schedule 19.08.2008    source источник


Ответы (13)


Библиотека времени выполнения Java поддерживает проверку. В последний раз я проверял, что это парсер Apache Xerces. Вероятно, вам следует использовать javax.xml. валидация.Validator.

import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*;
import java.net.URL;
import org.xml.sax.SAXException;
//import java.io.File; // if you use File
import java.io.IOException;
...
URL schemaFile = new URL("http://host:port/filename.xsd");
// webapp example xsd: 
// URL schemaFile = new URL("http://java.sun.com/xml/ns/j2ee/web-apphttp://www.w3.org/2001/XMLSchema4.xsd");
// local file example:
// File schemaFile = new File("/location/to/localfile.xsd"); // etc.
Source xmlFile = new StreamSource(new File("web.xml"));
SchemaFactory schemaFactory = SchemaFactory
    .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
try {
  Schema schema = schemaFactory.newSchema(schemaFile);
  Validator validator = schema.newValidator();
  validator.validate(xmlFile);
  System.out.println(xmlFile.getSystemId() + " is valid");
} catch (SAXException e) {
  System.out.println(xmlFile.getSystemId() + " is NOT valid reason:" + e);
} catch (IOException e) {}

Фабричная константа схемы - это строка http://www.w3.org/2001/XMLSchema, которая определяет XSD. Приведенный выше код проверяет дескриптор развертывания WAR по URL-адресу http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd, но вы также можете легко проверить его по локальному файлу.

Вы не должны использовать DOMParser для проверки документа (если ваша цель в любом случае не создать объектную модель документа). Это приведет к созданию объектов DOM по мере анализа документа - расточительно, если вы не собираетесь их использовать.

person McDowell    schedule 19.08.2008
comment
Используете ли вы в этом примере парсер DOM или SAX? Как мне узнать, какой парсер вы используете, если я не вижу ссылки ни на один из них. - person ziggy; 21.07.2012
comment
@ziggy - это деталь реализации реализации JAXP. Sun JDK 6 использует синтаксический анализатор SAX с StreamSource. Реализация JAXP может законно использовать парсер DOM в этом случае, но нет причин для этого. Если вы явно используете парсер DOM для проверки, вы обязательно создадите экземпляр дерева DOM. - person McDowell; 21.07.2012
comment
Как мне использовать ErrorHandler с вышеуказанным? Можно ли просто создать ErrorHandler и связать его с валидатором? т.е. validator.SetErrorHandler (), как в примере в этом вопросе SO stackoverflow.com/questions/4864681/? - person ziggy; 22.07.2012
comment
Разве выполнение не должно использоваться только для особых ситуаций, а не для потока управления? - person mike; 19.07.2013
comment
Не будет ли этот код обнаруживать только фатальные ошибки? Если вы хотите иметь возможность отлавливать нефатальные ошибки (например, неструктурные), я думаю, вам нужно будет использовать ErrorHandler. - person matt forsythe; 31.05.2014
comment
Этот код не работает, если файл для проверки содержит объявление DOCTYPE, если кто-то знает почему? - person HugoPoi; 03.12.2014
comment
Если вас интересует, как выполнить проверку по набору локальных схем, взгляните на stackoverflow.com/questions/44996345/offline-xml-validation - person jschnasse; 25.01.2018
comment
Он работает нормально, но при сканировании куба Sonar он покажет отключение обработки внешнего объекта XML (XXE), и это блокирует код - person Rohit Maurya; 09.12.2020

Вот как это сделать с помощью Xerces2. Руководство по этому вопросу, здесь (требуется регистрация) .

Оригинальная атрибуция: явно скопировано здесь:

import org.apache.xerces.parsers.DOMParser;
import java.io.File;
import org.w3c.dom.Document;

public class SchemaTest {
  public static void main (String args[]) {
      File docFile = new File("memory.xml");
      try {
        DOMParser parser = new DOMParser();
        parser.setFeature("http://xml.org/sax/features/validation", true);
        parser.setProperty(
             "http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", 
             "memory.xsd");
        ErrorChecker errors = new ErrorChecker();
        parser.setErrorHandler(errors);
        parser.parse("memory.xml");
     } catch (Exception e) {
        System.out.print("Problem parsing the file.");
     }
  }
}
person SCdF    schedule 19.08.2008
comment
Парсер SAX был бы более эффективным - парсер DOM создает объекты DOM; расточительные операции в этом случае. - person McDowell; 18.09.2008
comment
Вопрос в том, чтобы проверить XML на XSD. В этом ответе вы идете дальше и получаете объект Parser, который не нужен, верно? - person Weslor; 29.10.2015
comment
ErrorChecker не может быть преобразован в тип .. отсутствует импорт? - person Alex; 11.03.2016

Мы собираем наш проект с помощью ant, поэтому мы можем использовать задачу schemavalidate для проверки наших файлов конфигурации:

<schemavalidate> 
    <fileset dir="${configdir}" includes="**/*.xml" />
</schemavalidate>

Теперь непослушные файлы конфигурации не помогут нашей сборке!

http://ant.apache.org/manual/Tasks/schemavalidate.html

person chickeninabiscuit    schedule 14.07.2011

Поскольку это популярный вопрос, я отмечу, что java также может проверять соответствие упомянутым xsd, например, если сам файл .xml указывает XSD в заголовке, используя xsi:schemaLocation или xsi:noNamespaceSchemaLocation (или xsi для определенных пространств имен) ex:

<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="http://www.example.com/document.xsd">
  ...

или schemaLocation (всегда список пространств имен для сопоставлений xsd)

<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.example.com/my_namespace http://www.example.com/document.xsd">
  ...

Другие ответы здесь также работают, потому что файлы .xsd сопоставляются с пространствами имен, объявленными в файле .xml, потому что они объявляют пространство имен, и если они совпадают с пространством имен в файле .xml, все в порядке. Но иногда бывает удобно иметь собственный преобразователь ...

Из документации javadocs: если вы создаете схему без указания URL-адреса, файла или источника, то язык Java создает схему, которая просматривает проверяемый документ, чтобы найти схему, которую он должен использовать. Например:

SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema = factory.newSchema();

и это работает для нескольких пространств имен и т. д. Проблема с этим подходом заключается в том, что xmlsns:xsi, вероятно, является сетевым местоположением, поэтому по умолчанию он будет выходить и попадать в сеть при каждой проверке, что не всегда оптимально.

Вот пример, который проверяет XML-файл на соответствие любым ссылкам XSD (даже если он должен вытащить их из сети):

  public static void verifyValidatesInternalXsd(String filename) throws Exception {
    InputStream xmlStream = new new FileInputStream(filename);
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setValidating(true);
    factory.setNamespaceAware(true);
    factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
                 "http://www.w3.org/2001/XMLSchema");
    DocumentBuilder builder = factory.newDocumentBuilder();
    builder.setErrorHandler(new RaiseOnErrorHandler());
    builder.parse(new InputSource(xmlStream));
    xmlStream.close();
  }

  public static class RaiseOnErrorHandler implements ErrorHandler {
    public void warning(SAXParseException e) throws SAXException {
      throw new RuntimeException(e);
    }
    public void error(SAXParseException e) throws SAXException {
      throw new RuntimeException(e);
    }
    public void fatalError(SAXParseException e) throws SAXException {
      throw new RuntimeException(e);
    }
  }

Вы можете избежать извлечения ссылочных XSD из сети, даже если xml-файлы ссылаются на URL-адреса, указав xsd вручную (см. Некоторые другие ответы здесь) или используя каталог XML преобразователь стилей. Spring, по-видимому, также может перехватывать URL-запросы для обслуживания локальных файлов для проверки. Или вы можете установить собственное через setResourceResolver, например:

Source xmlFile = new StreamSource(xmlFileLocation);
SchemaFactory schemaFactory = SchemaFactory
                                .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema();
Validator validator = schema.newValidator();
validator.setResourceResolver(new LSResourceResolver() {
  @Override
  public LSInput resolveResource(String type, String namespaceURI,
                                 String publicId, String systemId, String baseURI) {
    InputSource is = new InputSource(
                           getClass().getResourceAsStream(
                          "some_local_file_in_the_jar.xsd"));
                          // or lookup by URI, etc...
    return new Input(is); // for class Input see 
                          // https://stackoverflow.com/a/2342859/32453
  }
});
validator.validate(xmlFile);

См. Также здесь другое руководство.

Я считаю, что по умолчанию используется синтаксический анализ DOM, вы можете сделать что-то подобное с парсером SAX, который проверяет тоже saxReader.setEntityResolver(your_resolver_here);

person rogerdpack    schedule 19.12.2016
comment
У меня не работает, метод resolveResource () не вызывается, если он не установлен в schemaFactory, есть идеи? - person tomasb; 25.07.2018
comment
Незнаю, у меня работает. Убедитесь, что вы устанавливаете его через setResourceResolver, но кроме этого, возможно, откроете новый вопрос ... - person rogerdpack; 25.07.2018
comment
Воскрешая старый пост, я думаю, он должен читать xsi:schemaLocation вместо xsi:SchemaLocation - дело имеет значение. См. w3.org/TR/xmlschema-1/#d0e3067. - person Christian Schlichtherle; 09.09.2020

Используя Java 7, вы можете следовать документации, представленной в описание пакета.

// create a SchemaFactory capable of understanding WXS schemas
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

// load a WXS schema, represented by a Schema instance
Source schemaFile = new StreamSource(new File("mySchema.xsd"));
Schema schema = factory.newSchema(schemaFile);

// create a Validator instance, which can be used to validate an instance document
Validator validator = schema.newValidator();

// validate the DOM tree
try {
    validator.validate(new StreamSource(new File("instance.xml"));
} catch (SAXException e) {
    // instance document is invalid!
}
person Paulo Fidalgo    schedule 13.05.2013
comment
Использование Java 7 .. Это фактически было включено в Java 5. - person Andrew Thompson; 21.08.2013
comment
Это в основном то же самое, что и принятый ответ. Это решение кажется мне немного неэффективным, поскольку оно излишне создает DOM для синтаксического анализа xml: parser.parse(new File("instance.xml")). validator принимает Source, поэтому вы можете: validator.validate(new StreamSource(new File("instance.xml"))). - person Alberto; 17.07.2014
comment
Работая таким образом, при первой ошибке в xml-файле будет выдано исключение SAXException, после чего проверка будет остановлена. Но я хочу знать все (!) Ошибки. Если вместо этого я использую ErrorHandler (собственный класс, реализующий ErrorHandler), он распознает все ошибки, но блок try-catch для validator.validate не генерирует никаких исключений .. Как распознать ошибку в классе, который вызывает проверку -метод моего валидатора? Спасибо за вашу помощь! - person mrbela; 13.01.2015
comment
Есть ошибки (например, ошибки проверки) и фатальные ошибки (ошибки правильного формата). Обычно анализ останавливается из-за одной фатальной ошибки. Но ошибка проверки не останавливает его: вы должны явно выбросить исключение. Таким образом, необходимо предоставить ErrorHandler, если вам нужно выполнить проверку. - person Ludovic Kuty; 22.10.2017
comment
Должен признать, код выглядит чище и легче читается, чем принятый ответ. - person Clockwork; 17.01.2019
comment
В строке проверки отсутствует закрывающая скобка. - person ceving; 17.08.2020

Если у вас есть Linux-Machine, вы можете использовать бесплатный инструмент командной строки SAXCount. Я нашел это очень полезным.

SAXCount -f -s -n my.xml

Он проверяется на соответствие dtd и xsd. 5s для файла размером 50 МБ.

В debian squeeze он находится в пакете libxerces-c-samples.

Определение dtd и xsd должно быть в xml! Вы не можете настроить их отдельно.

person juwens    schedule 22.03.2012
comment
Это позволяет выполнять простую проверку XML из vim (:! SAXCount -f -n -s%) - person Shane; 18.07.2012
comment
или воспользуйтесь почтенным xmllint xmllint --schema phone.xsd phone.xml (из ответа 13ren) - person rogerdpack; 19.12.2016
comment
Хороший ответ для superuser.com - person ceving; 17.08.2020

Еще один ответ: поскольку вы сказали, что вам нужно проверять файлы, которые вы генерируете (пишете), вы можете проверить контент во время записи, вместо того, чтобы сначала писать, а затем читать обратно для проверки. Вероятно, вы можете сделать это с помощью JDK API для проверки XML, если вы используете средство записи на основе SAX: если да, просто свяжите валидатор, вызвав 'Validator.validate (source, result)', где источник поступает от вашего писателя, а результат куда должен идти вывод.

В качестве альтернативы, если вы используете Stax для написания контента (или библиотеку, которая использует или может использовать stax), Woodstox может также напрямую поддерживают проверку при использовании XMLStreamWriter. Вот запись в блоге, показывающая, как это делается:

person StaxMan    schedule 27.03.2009
comment
Привет, StaxMan, есть ли какие-нибудь XMLStreamWriters, которые делают красивые отступы? Я был удивлен, что его нет в стандартной реализации. Кроме того, от этого много пользы? Я думаю, что это правильный путь, но, похоже, к нему очень мало интереса. - person 13ren; 28.03.2009
comment
только что нашел здесь ваше сообщение о StaxMate (но это не XMLStreamWriter): stackoverflow.com/questions/290326/stax-xml-formatting-in-java/ - person 13ren; 28.03.2009
comment
Да, StaxMate может это сделать. Он использует XMLStreamWriter внутри для записи контента, поэтому вы также можете подключить валидатор таким образом. - person StaxMan; 01.04.2010

Если вы создаете файлы XML программно, вам может потребоваться библиотека XMLBeans. Используя инструмент командной строки, XMLBeans автоматически сгенерирует и упакует набор объектов Java на основе XSD. Затем вы можете использовать эти объекты для создания XML-документа на основе этой схемы.

Он имеет встроенную поддержку проверки схемы и может преобразовывать объекты Java в документ XML и наоборот.

Castor и JAXB - это другие библиотеки Java, которые служат той же цели, что и XMLBeans.

person Todd    schedule 28.01.2009

С JAXB вы можете использовать следующий код:

    @Test
public void testCheckXmlIsValidAgainstSchema() {
    logger.info("Validating an XML file against the latest schema...");

    MyValidationEventCollector vec = new MyValidationEventCollector();

    validateXmlAgainstSchema(vec, inputXmlFileName, inputXmlSchemaName, inputXmlRootClass);

    assertThat(vec.getValidationErrors().isEmpty(), is(expectedValidationResult));
}

private void validateXmlAgainstSchema(final MyValidationEventCollector vec, final String xmlFileName, final String xsdSchemaName, final Class<?> rootClass) {
    try (InputStream xmlFileIs = Thread.currentThread().getContextClassLoader().getResourceAsStream(xmlFileName);) {
        final JAXBContext jContext = JAXBContext.newInstance(rootClass);
        // Unmarshal the data from InputStream
        final Unmarshaller unmarshaller = jContext.createUnmarshaller();

        final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        final InputStream schemaAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(xsdSchemaName);
        unmarshaller.setSchema(sf.newSchema(new StreamSource(schemaAsStream)));

        unmarshaller.setEventHandler(vec);

        unmarshaller.unmarshal(new StreamSource(xmlFileIs), rootClass).getValue(); // The Document class is the root object in the XML file you want to validate

        for (String validationError : vec.getValidationErrors()) {
            logger.trace(validationError);
        }
    } catch (final Exception e) {
        logger.error("The validation of the XML file " + xmlFileName + " failed: ", e);
    }
}

class MyValidationEventCollector implements ValidationEventHandler {
    private final List<String> validationErrors;

    public MyValidationEventCollector() {
        validationErrors = new ArrayList<>();
    }

    public List<String> getValidationErrors() {
        return Collections.unmodifiableList(validationErrors);
    }

    @Override
    public boolean handleEvent(final ValidationEvent event) {
        String pattern = "line {0}, column {1}, error message {2}";
        String errorMessage = MessageFormat.format(pattern, event.getLocator().getLineNumber(), event.getLocator().getColumnNumber(),
                event.getMessage());
        if (event.getSeverity() == ValidationEvent.FATAL_ERROR) {
            validationErrors.add(errorMessage);
        }
        return true; // you collect the validation errors in a List and handle them later
    }
}
person razvanone    schedule 27.11.2017

Используя Woodstox, настройте синтаксический анализатор StAX для проверки соответствия вашей схеме и синтаксического анализа XML.

Если обнаружены исключения, XML недействителен, в противном случае он действителен:

// create the XSD schema from your schema file
XMLValidationSchemaFactory schemaFactory = XMLValidationSchemaFactory.newInstance(XMLValidationSchema.SCHEMA_ID_W3C_SCHEMA);
XMLValidationSchema validationSchema = schemaFactory.createSchema(schemaInputStream);

// create the XML reader for your XML file
WstxInputFactory inputFactory = new WstxInputFactory();
XMLStreamReader2 xmlReader = (XMLStreamReader2) inputFactory.createXMLStreamReader(xmlInputStream);

try {
    // configure the reader to validate against the schema
    xmlReader.validateAgainst(validationSchema);

    // parse the XML
    while (xmlReader.hasNext()) {
        xmlReader.next();
    }

    // no exceptions, the XML is valid

} catch (XMLStreamException e) {

    // exceptions, the XML is not valid

} finally {
    xmlReader.close();
}

Примечание. Если вам нужно проверить несколько файлов, попробуйте повторно использовать свои XMLInputFactory и XMLValidationSchema, чтобы повысить производительность.

person Loris Securo    schedule 21.09.2019

Вы ищете инструмент или библиотеку?

Что касается библиотек, то в значительной степени стандартом де-факто является Xerces2, в котором есть как C ++ и версии Java.

Однако будьте осторожны, это тяжелое решение. Но опять же, проверка XML по файлам XSD - довольно серьезная проблема.

Что касается инструмента, который сделает это за вас, XMLFox кажется достойным бесплатным решением, но не пользовался им лично, точно сказать не могу.

person Adam    schedule 19.08.2008

Проверять на соответствие онлайн-схемам

Source xmlFile = new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream("your.xml"));
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(Thread.currentThread().getContextClassLoader().getResource("your.xsd"));
Validator validator = schema.newValidator();
validator.validate(xmlFile);

Проверять по локальным схемам

Автономная проверка XML с помощью Java

person jschnasse    schedule 04.10.2018

Мне пришлось только один раз проверить XML на соответствие XSD, поэтому я попробовал XMLFox. Я нашел это очень запутанным и странным. Справочные инструкции не соответствовали интерфейсу.

В итоге я использовал LiquidXML Studio 2008 (v6), который был намного проще в использовании и более знакомым (пользовательский интерфейс очень похож на Visual Basic 2008 Express, который я часто использую). Недостаток: возможность проверки отсутствует в бесплатной версии, поэтому мне пришлось использовать 30-дневную пробную версию.

person KnomDeGuerre    schedule 01.10.2008
comment
Вопрос в Java, а на этот ответ - нет. :-( - person james.garriss; 07.10.2015
comment
Честно говоря, слово java никогда не появляется в вопросе, только теги. Для этого я бы задала вопрос, а не ответ. - person Mark Storer; 08.12.2018
comment
Спасибо, Джеймс и Марк, помогите мне стать точнее! - person Knom; 10.12.2018