Який найкращий спосіб перевірити файл XML щодо файлу XSD?


263

Я генерую кілька файлів xml, які повинні відповідати файлу xsd, який мені було надано. Який найкращий спосіб перевірити їх відповідність?

Відповіді:


336

Бібліотека виконання 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) {}

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

Не слід використовувати DOMParser для перевірки документа (якщо ваша мета все-таки не є створенням об'єктної моделі документа). Це почне створювати об'єкти DOM під час аналізу документа - марно, якщо ви не збираєтесь їх використовувати.


Чи використовуєте парсер DOM або SAX у цьому прикладі? Як я можу сказати, який парсер ви використовуєте, як я не можу бачити посилання на будь-який.
зиггі

1
@ziggy - це детальна інформація про реалізацію JAXP . JDK 6 Sun використовує SAX-аналізатор з StreamSource . Реалізація JAXP могла б легально використовувати аналізатор DOM в цьому випадку, але немає підстав для цього. Якщо ви будете використовувати явно аналізатор DOM для перевірки, ви обов'язково інстанціюєте дерево DOM.
Макдауелл

Як я можу використовувати ErrorHandler із зазначеним вище? Чи є випадком просто створення ErrorHandler та асоціювання його з валідатором? тобто validator.SetErrorHandler (), як у прикладі в цьому запитанні ТАК stackoverflow.com/questions/4864681/… ?
зиггі

Чи не слід застосовувати виключення лише у ситуаціях, а не для контролю потоку?
Майк

Невже цей код не може спричинити лише фатальні помилки? Якщо ви хочете мати можливість спіймати нефатальні (такі як неструктурні), я думаю, вам потрібно буде використовувати ErrorHandler.
матова форзація

25

Ось як це зробити за допомогою 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.");
     }
  }
}

9
Аналізатор SAX був би більш ефективним - аналізатор DOM створює об'єкти DOM; марні операції в цьому випадку.
Макдауелл

Питання полягає у валідації XML щодо XSD. У цій відповіді ви йдете далі і отримуєте об'єкт Парсер, який не потрібен, правда?
Веслор

"Виправлено помилку помилки ErrorChecker до типу" .. відсутні імпорт?
Алекс

20

Ми будуємо наш проект за допомогою мурашника, тому ми можемо використовувати завдання schemavalidate для перевірки наших конфігураційних файлів:

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

Тепер неслухняні конфігураційні файли не зможуть побудувати!

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


13

Оскільки це популярне питання, я зазначу, що java також може перевірити "посилання на" xsd, наприклад, якщо файл .xml сам визначає XSD у заголовку, використовуючи xsi:SchemaLocationабо xsi:noNamespaceSchemaLocation(або xsi для конкретних просторів імен), наприклад :

<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" . Весна, очевидно, також може перехопити запити 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);


Не працює для мене, метод resolutionResource () не викликається, якщо його встановлено на schemaFactory, будь-яка ідея?
tomasb

Данно, працює на мене. Переконайтеся, що ви встановлюєте це, setResourceResolverале поза цим, можливо, відкрийте нове запитання ...
rogerdpack

6

За допомогою 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!
}

2
"Використання Java 7 .." Це фактично було включено в Java 5 .
Ендрю Томпсон

4
Це в основному те саме, що прийнята відповідь . Це рішення мені здається трохи неефективним , хоча, як це зайве будує DOM для XML для синтаксичного аналізу: parser.parse(new File("instance.xml")). validatorПриймає Source, так що ви можете: validator.validate(new StreamSource(new File("instance.xml"))).
Альберто

Працюючи таким чином, SAXException буде кинуто при першій помилці у xml-файлі і зупиняє потім перевірку. Але я хочу знати всі (!) Помилки. Якщо я використовую ErrorHandler (власний клас, який реалізує ErrorHandler) замість цього, він розпізнає всі помилки, але пробний блок catchator.validate не викидає жодного винятку. Як визнати помилку в класі, що викликає валідацію -метод мого валідатора? Спасибі за вашу допомогу!
mrbela

Існують "помилки" (наприклад, помилки перевірки) та "фатальні помилки" (добре сформовані помилки). Одна фатальна помилка, як правило, зупиняє розбір. Але помилка перевірки не зупиняє її: ви повинні явно кинути виняток. Таким чином, необхідно вказати, ErrorHandlerякщо вам потрібно зробити перевірку.
Людовик Куті

1
Маю визнати, що на цей код виглядає чистіше і простіше, ніж прийнята відповідь.
Годинник

3

Якщо у вас є Linux-машина, ви можете використовувати безкоштовний інструмент командного рядка SAXCount. Я вважав це дуже корисним.

SAXCount -f -s -n my.xml

Він підтверджує проти dtd та xsd. 5s для файлу 50MB.

У дебіанському стисканні він знаходиться в пакеті "libxerces-c-sample".

Визначення dtd та xsd має бути у xml! Ви не можете їх конфігурувати окремо.


2
Це дозволяє просту перевірку XML від vim (:! SAXCount -f -n -s%)
Шейн

4
або скористайтеся поважним xmllint xmllint --schema phone.xsd phone.xml(з відповіді 13рен)
rogerdpack

3

Ще одна відповідь: оскільки ви сказали, що вам потрібно перевірити файли, які ви створюєте (пишете), ви можете перевірити вміст під час написання, замість того, щоб спочатку писати, а потім читати назад для перевірки. Ви можете, ймовірно, зробити це за допомогою JDK API для перевірки Xml, якщо ви використовуєте письменник на основі SAX: якщо так, просто зв’яжіть у валідаторі, зателефонувавши "Validator.validate (джерело, результат)", де джерело надходить від вашого автора, а результат - куди потрібно йти вихід.

Якщо ви використовуєте Stax для написання вмісту (або бібліотеки, яка використовує або може використовувати stax), Woodstox також може безпосередньо підтримувати перевірку при використанні XMLStreamWriter. Ось запис у блозі, який показує, як це робиться:


Привіт, StaxMan, чи є XMLStreamWriters, які роблять симпатичні друковані відступи? Я був здивований, що це не в стандартній реалізації. Крім того, чи отримує це багато користі? Я думаю, що це правильний шлях, але інтерес до цього здається дуже малим.
13рен

тільки що знайшов ваш пост тут про StaxMate (але це не XMLStreamWriter): stackoverflow.com/questions/290326/stax-xml-formatting-in-java / ...
13ren

Так, StaxMate може це зробити. Він використовує XMLStreamWriter внутрішньо для запису вмісту, тому ви можете підключити валідатор і таким чином.
StaxMan

2

Якщо ви генеруєте XML-файли програмно, можливо, захочете переглянути бібліотеку XMLBeans . Використовуючи інструмент командного рядка, XMLBeans автоматично генерує та пакує набір об’єктів Java на основі XSD. Потім ви можете використовувати ці об’єкти для створення XML-документа на основі цієї схеми.

Він має вбудовану підтримку перевірки схеми і може конвертувати об'єкти Java в XML-документ і навпаки.

Castor і JAXB - це інші бібліотеки Java, які служать аналогічним для XMLBeans.


1

З 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, який має як версії C ++, так і Java .

Попереджуйте, що це важке рішення. Але знову ж таки, перевірка XML щодо XSD-файлів - досить важка проблема.

Що стосується інструменту зробити це для вас, XMLFox здається гідним безкоштовним рішенням, але не використовуючи його особисто, я не можу сказати точно.


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

За допомогою 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з метою максимальної ефективності.


-3

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

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


1
Питання - Java, але ця відповідь - ні. :-(
james.garriss

Чесно кажучи, слово "java" ніколи не з’являється у питанні, лише теги. Я б поставив на це запитання, а не відповідь.
Марк Сторер

Дякую Джеймс та Марку, допоможи мені загострити!
Кном
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.