Каков наилучший способ проверки XML-файла по сравнению с XSD-файлом?

256

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

Теги:
validation
xsd

13 ответов

297
Лучший ответ

Библиотека времени выполнения Java поддерживает проверку. В прошлый раз, когда я проверил, это был парсер Apache Xerces под обложками. Вероятно, вы должны использовать javax.xml.validation.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-app_2_4.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) {}

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

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

  • 38
    Часы в сети пытаются найти это; почему я еще не научился искать так сначала?
  • 0
    Используете ли вы DOM или SAX парсер в этом примере? Как мне сказать, какой парсер вы используете, так как я не вижу ссылки ни на один из них.
Показать ещё 7 комментариев
26

Вот как это сделать с помощью 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.");
     }
  }
}
  • 7
    Парсер SAX был бы более эффективен - парсер DOM создает объекты DOM; расточительные операции в этом случае.
  • 0
    Вопрос заключается в проверке XML на соответствие XSD. В этом ответе вы идете дальше и получаете объект Parser, который не нужен, верно?
Показать ещё 1 комментарий
19

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

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

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

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

11

Поскольку это популярный вопрос, я укажу, что 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 
                          // /questions/205059/how-to-validate-an-xml-file-using-java-with-an-xsd-having-an-include/1111642#1111642
  }
});
validator.validate(xmlFile);

Смотрите также здесь для другого учебника.

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

  • 0
    У меня не работает, метод resolResource () не вызывается, если он не установлен на schemaFactory, есть идеи?
  • 0
    Незнайка, у меня работает. Убедитесь, что вы устанавливаете его через setResourceResolver но помимо этого, возможно, откройте новый вопрос ...
9

Я также нашел этот сайт полезным.

http://www.ibm.com/developerworks/xml/library/x-javaxmlvalidapi.html

Это тот, который действительно работал у меня с минимумом суеты.

5

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

// parse an XML document into a DOM tree
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = parser.parse(new File("instance.xml"));

// 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 DOMSource(document));
} catch (SAXException e) {
    // instance document is invalid!
}
  • 2
    «Использование Java 7 ..» Это было на самом деле включено в Java 5 .
  • 3
    Это в основном так же, как принятый ответ . Это решение кажется мне немного неэффективным, поскольку оно излишне создает DOM для синтаксического анализа xml: parser.parse(new File("instance.xml")) . validator принимает Source , поэтому вы можете: validator.validate(new StreamSource(new File("instance.xml"))) .
Показать ещё 3 комментария
3

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

SAXCount -f -s -n my.xml

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

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

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

  • 2
    Это позволяет выполнять простую проверку XML из vim (:! SAXCount -f -n -s%)
  • 4
    или используйте почтенный xmllint xmllint --schema phone.xsd phone.xml (из ответа 13ren)
3

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

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

  • 0
    Привет, StaxMan, есть ли какие-нибудь XMLStreamWriters, которые делают отступы при красивой печати? Я был удивлен, что это не в стандартной реализации. Кроме того, это получает много пользы? Я думаю, что это правильный путь, но, похоже, это мало интересует.
  • 0
    только что нашел ваш пост здесь о StaxMate (но это не XMLStreamWriter): stackoverflow.com/questions/290326/stax-xml-formatting-in-java/…
Показать ещё 1 комментарий
2

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

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

Castor и JAXB это другие библиотеки Java, которые аналогично используют XMLBeans.

0

Проверка по сетевым схемам

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

0

С помощью 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
    }
}
0

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

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

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

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

-3

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

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

  • 0
    Вопрос в Java, но это не так. :-(
  • 0
    Чтобы быть справедливым, слово «java» никогда не появляется в вопросе, только теги. Я бы ответил на этот вопрос, а не на ответ.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню