Apa cara terbaik untuk memvalidasi file XML terhadap file XSD?

263

Saya membuat beberapa file xml yang perlu disesuaikan dengan file xsd yang diberikan kepada saya. Apa cara terbaik untuk memverifikasi kepatuhan mereka?

Jeff
sumber

Jawaban:

336

Pustaka runtime Java mendukung validasi. Terakhir kali saya memeriksa ini adalah parser Apache Xerces di bawah selimut. Anda mungkin harus menggunakan 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) {}

Konstanta pabrik skema adalah string http://www.w3.org/2001/XMLSchemayang mendefinisikan XSD. Kode di atas memvalidasi deskriptor penyebaran WAR terhadap URL http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsdtetapi Anda bisa dengan mudah memvalidasi terhadap file lokal.

Anda tidak boleh menggunakan DOMParser untuk memvalidasi dokumen (kecuali jika tujuan Anda adalah membuat model objek dokumen). Ini akan mulai membuat objek DOM karena mem-parsing dokumen - boros jika Anda tidak akan menggunakannya.

McDowell
sumber
Apakah Anda menggunakan parser DOM atau SAX dalam contoh ini? Bagaimana saya tahu parser yang Anda gunakan karena saya tidak bisa melihat referensi untuk keduanya.
ziggy
1
@ziggy - ini adalah detail implementasi dari implementasi JAXP . Sun's JDK 6 menggunakan SAX parser dengan StreamSource . Implementasi JAXP secara hukum dapat menggunakan parser DOM dalam kasus ini, tetapi tidak ada alasan untuk melakukannya. Jika kamu menggunakan parser DOM secara eksplisit untuk validasi, Anda pasti akan membuat instance pohon DOM.
McDowell
Bagaimana cara menggunakan ErrorHandler dengan yang di atas? Apakah hanya membuat ErrorHandler dan mengaitkannya dengan validator? yaitu validator.SetErrorHandler () seperti pada contoh dalam pertanyaan SO ini stackoverflow.com/questions/4864681/… ?
ziggy
Seharusnya eksekusi tidak hanya digunakan untuk situasi eksekusi dan tidak untuk aliran kontrol?
mike
Bukankah kode ini hanya menangkap kesalahan fatal? Jika Anda ingin dapat menangkap non-fatals (seperti yang non-struktural), saya pikir Anda perlu menggunakan ErrorHandler.
matt forsythe
25

Berikut cara melakukannya menggunakan Xerces2 . Tutorial untuk ini, di sini (daftar persyaratan).

Atribusi asli: disalin secara terang-terangan dari sini :

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.");
     }
  }
}
SCdF
sumber
9
Parser SAX akan lebih efisien - parser DOM menciptakan objek DOM; operasi boros dalam hal ini.
McDowell
Pertanyaannya adalah untuk memvalidasi XML terhadap XSD. Dalam jawaban ini Anda melangkah lebih jauh dan mendapatkan objek Parser, yang tidak diperlukan, bukan?
Weslor
"ErrorChecker tidak dapat diselesaikan ke jenis" .. hilang impor?
Alex
20

Kami membangun proyek kami menggunakan semut, sehingga kami dapat menggunakan tugas schemavalidate untuk memeriksa file konfigurasi kami:

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

Sekarang file konfigurasi nakal akan gagal membangun kita!

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

chickeninabiscuit
sumber
13

Karena ini adalah pertanyaan yang populer, saya akan menunjukkan bahwa java juga dapat memvalidasi terhadap xsd "dirujuk", misalnya jika file .xml sendiri menentukan XSD di header, menggunakan xsi:SchemaLocationatau xsi:noNamespaceSchemaLocation(atau xsi untuk ruang nama tertentu) ex :

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

atau SchemaLocation (selalu daftar namespace ke pemetaan 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">
  ...

Jawaban lain juga berfungsi di sini, karena file .xsd "memetakan" ke ruang nama yang dinyatakan dalam file .xml, karena mereka mendeklarasikan namespace, dan jika cocok dengan namespace dalam file .xml, Anda baik. Namun terkadang lebih mudah untuk memiliki penyelesai khusus ...

Dari javadocs: "Jika Anda membuat skema tanpa menentukan URL, file, atau sumber, maka bahasa Java membuat yang terlihat dalam dokumen yang divalidasi untuk menemukan skema yang harus digunakan. Misalnya:"

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

dan ini berfungsi untuk banyak namespaces, dll. Masalah dengan pendekatan ini adalah bahwa xmlsns:xsiitu mungkin lokasi jaringan, jadi secara default akan keluar dan tekan jaringan dengan masing-masing dan setiap validasi, tidak selalu optimal.

Berikut ini contoh yang memvalidasi file XML terhadap referensi XSD apa pun (bahkan jika harus menariknya dari jaringan):

  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);
    }
  }

Anda dapat menghindari menarik XSD yang dirujuk dari jaringan, meskipun url referensi file xml, dengan menentukan xsd secara manual (lihat beberapa jawaban lain di sini) atau dengan menggunakan pemecah gaya "Katalog XML" . Spring tampaknya juga dapat mencegat permintaan URL untuk melayani file lokal untuk validasi. Atau Anda dapat mengatur sendiri melalui setResourceResolver , mis:

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);

Lihat juga di sini untuk tutorial lainnya.

Saya percaya standarnya adalah dengan menggunakan parsing DOM, Anda dapat melakukan sesuatu yang mirip dengan parser SAX yang memvalidasi juga saxReader.setEntityResolver(your_resolver_here);

rogerdpack
sumber
Tidak berfungsi untuk saya, metode resolResource () tidak dipanggil kecuali ditetapkan pada schemaFactory, ada ide?
tomasb
Entahlah, bekerja untuk saya. Pastikan Anda mengaturnya melalui setResourceResolvertetapi di luar itu, mungkin membuka pertanyaan baru ...
rogerdpack
6

Menggunakan Java 7 Anda dapat mengikuti dokumentasi yang disediakan dalam deskripsi paket .

// 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!
}
Paulo Fidalgo
sumber
2
"Menggunakan Java 7 .." Itu sebenarnya termasuk dalam Java 5 .
Andrew Thompson
4
Ini pada dasarnya sama dengan jawaban yang diterima . Solusi ini tampaknya saya sedikit tidak efisien meskipun, karena itu tidak perlu membangun DOM untuk xml untuk parse: parser.parse(new File("instance.xml")). The validatormenerima Source, sehingga Anda dapat: validator.validate(new StreamSource(new File("instance.xml"))).
Alberto
Bekerja dengan cara ini, SAXException akan dilempar pada kesalahan pertama dalam file xml dan berhenti kemudian validasi. Tapi saya ingin tahu semua kesalahan (!). Jika saya menggunakan ErrorHandler (kelas sendiri yang mengimplementasikan ErrorHandler) sebagai gantinya, ia mengenali semua kesalahan, tetapi blok catch-catch-validator.validate tidak membuang Pengecualian apa pun .. Bagaimana cara mengenali kesalahan di kelas yang memanggil validasi -metode validator saya? Terima kasih atas bantuan Anda!
mrbela
Ada "kesalahan" (mis kesalahan validasi) dan "kesalahan fatal" (kesalahan well-formedness). Satu kesalahan fatal biasanya menghentikan penguraian. Tetapi kesalahan validasi tidak menghentikannya: Anda harus secara eksplisit melempar pengecualian. Dengan demikian, perlu untuk menyediakan ErrorHandlerjika Anda perlu melakukan validasi.
Ludovic Kuty
1
Harus mengakui, kode terlihat lebih bersih dan lebih mudah dibaca daripada jawaban yang diterima.
Clockwork
3

Jika Anda memiliki Mesin Linux, Anda dapat menggunakan alat baris perintah SAXCount gratis. Saya menemukan ini sangat berguna.

SAXCount -f -s -n my.xml

Ini memvalidasi terhadap dtd dan xsd. 5s untuk file 50MB.

Dalam debian squeeze terletak di paket "libxerces-c-samples".

Definisi dtd dan xsd harus dalam xml! Anda tidak dapat mengonfigurasinya secara terpisah.

remaja
sumber
2
Ini memungkinkan validasi XML sederhana dari vim (:! SAXCount -f -n -s%)
Shane
4
atau gunakan xmllint yang terhormat xmllint --schema phone.xsd phone.xml(dari jawaban oleh 13ren)
rogerdpack
3

Satu jawaban lagi: karena Anda mengatakan Anda perlu memvalidasi file yang Anda hasilkan (menulis), Anda mungkin ingin memvalidasi konten saat Anda menulis, alih-alih menulis terlebih dahulu, lalu membaca kembali untuk validasi. Anda mungkin dapat melakukannya dengan JDK API untuk validasi Xml, jika Anda menggunakan penulis berbasis SAX: jika demikian, cukup tautkan dalam validator dengan memanggil 'Validator.validate (sumber, hasil)', di mana sumber berasal dari penulis Anda, dan hasilnya adalah di mana output harus pergi.

Atau jika Anda menggunakan Stax untuk menulis konten (atau perpustakaan yang menggunakan atau dapat menggunakan stax), Woodstox juga dapat langsung mendukung validasi saat menggunakan XMLStreamWriter. Berikut ini entri blog yang menunjukkan bagaimana hal itu dilakukan:

StaxMan
sumber
Hai StaxMan, apakah ada XMLStreamWriter yang melakukan indentasi cetak-cantik? Saya terkejut bahwa itu tidak dalam implementasi standar. Juga, apakah semakin banyak digunakan? Saya pikir itu cara yang tepat untuk pergi, tetapi tampaknya ada sedikit minat di dalamnya.
13ren
baru saja menemukan posting Anda di sini tentang StaxMate (tapi itu bukan XMLStreamWriter): stackoverflow.com/questions/290326/stax-xml-formatting-in-java/…
13ren
Ya, StaxMate bisa melakukan itu. Ini menggunakan XMLStreamWriter secara internal untuk menulis konten, sehingga Anda dapat menghubungkan validator juga.
StaxMan
2

Jika Anda membuat file XML secara terprogram, Anda mungkin ingin melihat perpustakaan XMLBeans . Menggunakan alat baris perintah, XMLBeans akan secara otomatis menghasilkan dan mengemas satu set objek Java berdasarkan XSD. Anda kemudian dapat menggunakan objek-objek ini untuk membangun dokumen XML berdasarkan skema ini.

Ini memiliki dukungan bawaan untuk validasi skema, dan dapat mengonversi objek Java ke dokumen XML dan sebaliknya.

Castor dan JAXB adalah perpustakaan Java lain yang memiliki tujuan yang mirip dengan XMLBeans.

Todd
sumber
1

Dengan JAXB, Anda dapat menggunakan kode di bawah ini:

    @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
    }
}
Razvanone
sumber
0

Apakah Anda mencari alat atau perpustakaan?

Sejauh perpustakaan berjalan, standar de-facto cukup banyak adalah Xerces2 yang memiliki kedua versi C ++ dan Java .

Sebelum diperingatkan, itu adalah solusi berat. Tetapi sekali lagi, memvalidasi XML terhadap file XSD adalah masalah berat yang agak berat.

Adapun alat untuk melakukan ini untuk Anda, XMLFox tampaknya menjadi solusi freeware yang layak, tetapi tidak menggunakannya secara pribadi saya tidak bisa mengatakan dengan pasti.

Adam
sumber
0

Validasi terhadap skema online

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);

Validasi terhadap skema lokal

Validasi XML Offline dengan Java

Jschnasse
sumber
0

Menggunakan Woodstox , konfigurasikan pengurai StAX untuk memvalidasi terhadap skema Anda dan parsing XML.

Jika pengecualian tertangkap XML tidak valid, jika tidak maka valid:

// 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();
}

Catatan : Jika Anda perlu memvalidasi banyak file, Anda harus mencoba untuk menggunakan kembali XMLInputFactorydan XMLValidationSchemauntuk memaksimalkan kinerja.

Loris Securo
sumber
-3

Saya harus memvalidasi XML terhadap XSD hanya satu kali, jadi saya mencoba XMLFox. Saya menemukan itu sangat membingungkan dan aneh. Instruksi bantuan sepertinya tidak cocok dengan antarmuka.

Saya akhirnya menggunakan LiquidXML Studio 2008 (v6) yang jauh lebih mudah digunakan dan lebih akrab (UI sangat mirip dengan Visual Basic 2008 Express, yang sering saya gunakan). Kekurangannya: kemampuan validasi tidak dalam versi gratis, jadi saya harus menggunakan uji coba 30 hari.

KnomDeGuerre
sumber
1
Pertanyaannya adalah Java, tetapi jawaban ini tidak. :-(
james.garriss
Agar adil, kata "java" tidak pernah muncul dalam pertanyaan, hanya tag. Saya akan menjawab pertanyaan untuk itu, bukan jawabannya.
Mark Storer
Terima kasih james dan Mark, bantu saya mengasah!
Knom