Який найкращий спосіб перевірити файл 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.