Wie kann eine XML-Datei am besten anhand einer XSD-Datei überprüft werden?

263

Ich generiere einige XML-Dateien, die einer mir übergebenen xsd-Datei entsprechen müssen. Wie kann die Konformität am besten überprüft werden?

Jeff
quelle

Antworten:

336

Die Java-Laufzeitbibliothek unterstützt die Validierung. Als ich das letzte Mal nachgesehen habe, war dies der Apache Xerces-Parser unter der Decke. Sie sollten wahrscheinlich einen javax.xml.validation.Validator verwenden .

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

Die Schema-Factory-Konstante ist die Zeichenfolge, http://www.w3.org/2001/XMLSchemadie XSDs definiert. Der obige Code validiert einen WAR-Bereitstellungsdeskriptor anhand der URL. http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsdSie können ihn jedoch genauso einfach anhand einer lokalen Datei validieren.

Sie sollten den DOMParser nicht zum Validieren eines Dokuments verwenden (es sei denn, Sie möchten ohnehin ein Dokumentobjektmodell erstellen). Dadurch werden DOM-Objekte erstellt, während das Dokument analysiert wird - verschwenderisch, wenn Sie sie nicht verwenden.

McDowell
quelle
Verwenden Sie in diesem Beispiel einen DOM- oder SAX-Parser? Wie kann ich feststellen, welchen Parser Sie verwenden, da ich keinen Verweis darauf sehe?
Ziggy
1
@ziggy - Dies ist ein Implementierungsdetail der JAXP-Implementierung . Das JDK 6 von Sun verwendet den SAX-Parser mit einer StreamSource . Eine JAXP-Implementierung könnte in diesem Fall legal einen DOM-Parser verwenden, es gibt jedoch keinen Grund dafür. Wenn Sie einen DOM-Parser explizit zur Validierung verwenden, werden Sie definitiv einen DOM-Baum instanziieren.
McDowell
Wie verwende ich einen ErrorHandler mit den oben genannten? Geht es nur darum, den ErrorHandler zu erstellen und ihn dem Validator zuzuordnen? dh validator.SetErrorHandler () wie im Beispiel in dieser SO-Frage stackoverflow.com/questions/4864681/… ?
Zickzack
Sollten Ausführungen nicht nur für Ausführungssituationen und nicht für den Kontrollfluss verwendet werden?
Mike
Wird dieser Code nicht nur schwerwiegende Fehler abfangen? Wenn Sie in der Lage sein möchten, Nicht-Fatale (wie nicht-strukturelle) zu fangen, müssen Sie wahrscheinlich einen ErrorHandler verwenden.
Matt Forsythe
25

Hier erfahren Sie, wie Sie dies mit Xerces2 tun . Ein Tutorial dazu hier (Anmeldung erforderlich).

Ursprüngliche Zuschreibung: offensichtlich von hier kopiert :

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
quelle
9
Der SAX-Parser wäre effizienter - der DOM-Parser erstellt DOM-Objekte. verschwenderische Operationen in diesem Fall.
McDowell
Die Frage ist, ein XML gegen eine XSD zu validieren. In dieser Antwort gehen Sie weiter und erhalten ein Parser-Objekt, das nicht benötigt wird, oder?
Weslor
"ErrorChecker kann nicht in einen Typ aufgelöst werden" .. fehlender Import?
Alex
20

Wir erstellen unser Projekt mit ant, damit wir die Task schemavalidate verwenden können, um unsere Konfigurationsdateien zu überprüfen:

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

Jetzt werden freche Konfigurationsdateien unseren Build nicht bestehen!

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

Hühnchen-Keks
quelle
13

Da dies eine beliebte Frage ist, werde ich kann , dass Java auch darauf hinweist Validate gegen „bezeichnet“ xsd sind, zum Beispiel , wenn die XML - Datei selbst im Header XSD die angibt, mit xsi:SchemaLocationoder xsi:noNamespaceSchemaLocation(oder xsi für bestimmte Namensräume) ab :

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

oder SchemaLocation (immer eine Liste von Namespace-zu-xsd-Zuordnungen)

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

Die anderen Antworten funktionieren auch hier, da die .xsd-Dateien den in der .xml-Datei deklarierten Namespaces "zugeordnet" sind, weil sie einen Namespace deklarieren und wenn Sie mit dem Namespace in der .xml-Datei übereinstimmen, sind Sie gut. Aber manchmal ist es praktisch, einen benutzerdefinierten Resolver zu haben ...

Aus den Javadocs: "Wenn Sie ein Schema erstellen, ohne eine URL, Datei oder Quelle anzugeben, erstellt die Java-Sprache eine, die im zu überprüfenden Dokument nach dem zu verwendenden Schema sucht. Beispiel:"

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

und das funktioniert für mehrere Namespaces usw. Das Problem bei diesem Ansatz ist, dass xmlsns:xsies sich wahrscheinlich um einen Netzwerkspeicherort handelt, sodass er standardmäßig bei jeder Validierung ausgeht und das Netzwerk trifft, was nicht immer optimal ist.

Hier ist ein Beispiel, das eine XML-Datei anhand von XSDs überprüft, auf die sie verweist (auch wenn sie aus dem Netzwerk abgerufen werden muss):

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

Sie können vermeiden, referenzierte XSDs aus dem Netzwerk abzurufen, obwohl die XML-Dateien auf URLs verweisen, indem Sie die xsd manuell angeben (siehe einige andere Antworten hier) oder einen Resolver im Stil eines "XML-Katalogs" verwenden . Spring kann anscheinend auch die URL-Anforderungen abfangen , um lokale Dateien für Validierungen bereitzustellen. Oder Sie können Ihre eigenen über setResourceResolver festlegen , z.

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

Siehe auch hier für ein weiteres Tutorial.

Ich glaube , dass die Standard - DOM - Parsing zu verwenden ist, können Sie etwas ähnliches mit SAX - Parser tun können , das ist die Validierung als auch saxReader.setEntityResolver(your_resolver_here);

Rogerdpack
quelle
Funktioniert bei mir nicht, die Methode resolveResource () wird nur aufgerufen, wenn sie auf schemaFactory festgelegt ist.
Thomasb
Keine Ahnung, arbeitet für mich. Stellen Sie sicher, dass Sie es über setResourceResolveraber darüber hinaus einstellen , vielleicht öffnen Sie eine neue Frage ...
Rogerdpack
5

Mit Java 7 können Sie der Dokumentation in der Paketbeschreibung folgen .

// 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!
}
Paulo Fidalgo
quelle
2
"Verwenden von Java 7 .." Das war tatsächlich in Java 5 enthalten .
Andrew Thompson
3
Dies entspricht im Wesentlichen der akzeptierten Antwort . Diese Lösung scheint mir jedoch etwas ineffizient zu sein, da sie unnötigerweise das DOM für die zu analysierende XML erstellt : parser.parse(new File("instance.xml")). Das validatorakzeptiert a Source, damit Sie : validator.validate(new StreamSource(new File("instance.xml"))).
Alberto
Auf diese Weise wird beim ersten Fehler in der XML-Datei eine SAXException ausgelöst und die Validierung gestoppt. Aber ich möchte alle (!) Fehler wissen. Wenn ich stattdessen einen ErrorHandler (eine eigene Klasse, die ErrorHandler implementiert) verwende, erkennt er alle Fehler, aber der try-catch-Block von validator.validate löst keine Ausnahme aus. Wie erkenne ich einen Fehler in der Klasse, die den validate aufruft? -Methode meines Validators? Danke für Ihre Hilfe!
Mrbela
Es gibt "Fehler" (z. B. Validierungsfehler) und "schwerwiegende Fehler" (Formfehler). Ein schwerwiegender Fehler stoppt normalerweise die Analyse. Ein Validierungsfehler stoppt ihn jedoch nicht: Sie müssen explizit eine Ausnahme auslösen. Daher ist es erforderlich, eine anzugeben, ErrorHandlerwenn Sie eine Validierung durchführen müssen.
Ludovic Kuty
1
Ich muss zugeben, der Code sieht sauberer und leichter zu lesen aus als die akzeptierte Antwort.
Uhrwerk
3

Wenn Sie eine Linux-Maschine haben, können Sie das kostenlose Befehlszeilentool SAXCount verwenden. Ich fand das sehr nützlich.

SAXCount -f -s -n my.xml

Es wird gegen dtd und xsd validiert. 5s für eine 50MB Datei.

In Debian Squeeze befindet es sich im Paket "libxerces-c-samples".

Die Definition von dtd und xsd muss in der xml sein! Sie können sie nicht separat konfigurieren.

juwens
quelle
2
Dies ermöglicht eine einfache XML-Validierung von vim (:! SAXCount -f -n -s%)
Shane
4
oder verwenden Sie die ehrwürdige xmllint xmllint --schema phone.xsd phone.xml(aus einer Antwort von 13ren)
Rogerdpack
3

Noch eine Antwort: Da Sie gesagt haben, dass Sie die von Ihnen generierten (schreibenden) Dateien validieren müssen, möchten Sie möglicherweise den Inhalt während des Schreibens validieren, anstatt zuerst zu schreiben und dann zur Validierung zurückzulesen. Sie können dies wahrscheinlich mit der JDK-API für die XML-Validierung tun, wenn Sie einen SAX-basierten Writer verwenden: Wenn ja, verknüpfen Sie den Validator einfach, indem Sie 'Validator.validate (Quelle, Ergebnis)' aufrufen, wobei die Quelle von Ihrem Writer stammt und das Ergebnis ist wohin die Ausgabe gehen muss.

Wenn Sie Stax zum Schreiben von Inhalten verwenden (oder eine Bibliothek, die stax verwendet oder verwenden kann), kann Woodstox die Validierung auch direkt unterstützen, wenn Sie XMLStreamWriter verwenden. Hier ist ein Blogeintrag, der zeigt, wie das gemacht wird:

StaxMan
quelle
Hey StaxMan, gibt es XMLStreamWriter, die hübsche Einrückungen drucken? Ich war überrascht, dass es nicht in der Standardimplementierung ist. Wird es auch viel genutzt? Ich denke, es ist der richtige Weg, aber es scheint sehr wenig Interesse daran zu geben.
13ren
Ich habe gerade Ihren Beitrag hier über StaxMate gefunden (aber es ist kein XMLStreamWriter): stackoverflow.com/questions/290326/stax-xml-formatting-in-java/…
13ren
Ja, das kann StaxMate. Es verwendet XMLStreamWriter intern zum Schreiben von Inhalten, sodass Sie den Validator auch auf diese Weise anschließen können.
StaxMan
2

Wenn Sie XML-Dateien programmgesteuert generieren, sollten Sie sich die XMLBeans- Bibliothek ansehen . Mithilfe eines Befehlszeilentools generiert XMLBeans automatisch eine Reihe von Java-Objekten basierend auf einer XSD und packt sie zusammen. Mit diesen Objekten können Sie dann ein XML-Dokument erstellen, das auf diesem Schema basiert.

Es bietet integrierte Unterstützung für die Schemaüberprüfung und kann Java-Objekte in ein XML-Dokument konvertieren und umgekehrt.

Castor und JAXB sind andere Java-Bibliotheken, die einen ähnlichen Zweck wie XMLBeans erfüllen.

Todd
quelle
1

Mit JAXB können Sie den folgenden Code verwenden:

    @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
    }
}
Razvanon
quelle
0

Suchen Sie ein Tool oder eine Bibliothek?

In Bezug auf Bibliotheken ist Xerces2, das sowohl C ++ als auch Java- Versionen hat , so ziemlich der De-facto-Standard .

Seien Sie jedoch gewarnt, es ist eine schwere Lösung. Andererseits ist die Validierung von XML anhand von XSD-Dateien ein ziemlich schweres Problem.

Als Tool, das dies für Sie erledigt, scheint XMLFox eine anständige Freeware-Lösung zu sein, aber nachdem ich es nicht persönlich verwendet habe, kann ich nicht sicher sagen.

Adam
quelle
0

Validieren Sie anhand von Online-Schemas

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

Überprüfen Sie anhand lokaler Schemas

Offline-XML-Validierung mit Java

jschnasse
quelle
0

Konfigurieren Sie mit Woodstox den StAX-Parser so, dass er anhand Ihres Schemas überprüft und das XML analysiert wird.

Wenn Ausnahmen abgefangen werden, ist das XML ungültig, andernfalls ist es gültig:

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

Hinweis : Wenn Sie mehrere Dateien validieren müssen, sollten Sie versuchen, Ihre XMLInputFactoryund wiederzuverwenden XMLValidationSchema, um die Leistung zu maximieren.

Loris Securo
quelle
-3

Ich musste ein XML nur einmal gegen XSD validieren, also habe ich XMLFox ausprobiert. Ich fand es sehr verwirrend und seltsam. Die Hilfeanweisungen schienen nicht mit der Benutzeroberfläche übereinzustimmen.

Am Ende habe ich LiquidXML Studio 2008 (v6) verwendet, das viel einfacher zu verwenden und sofort vertrauter war (die Benutzeroberfläche ist Visual Basic 2008 Express sehr ähnlich, das ich häufig verwende). Der Nachteil: Die Validierungsfunktion ist nicht in der kostenlosen Version enthalten, daher musste ich die 30-Tage-Testversion verwenden.

KnomDeGuerre
quelle
1
Die Frage ist Java, aber diese Antwort ist nicht. :-(
james.garriss
Um fair zu sein, erscheint das Wort "Java" nie in der Frage, nur die Tags. Ich würde die Frage dafür stellen, nicht die Antwort.
Mark Storer
Danke James und Mark, hilf mir zu schärfen!
Knom