Jaki jest najlepszy sposób na sprawdzenie poprawności pliku XML względem pliku XSD?

263

Generuję niektóre pliki XML, które muszą być zgodne z plikiem xsd, który został mi przekazany. Jaki jest najlepszy sposób sprawdzenia, czy są zgodne?

Jeff
źródło

Odpowiedzi:

336

Biblioteka środowiska wykonawczego Java obsługuje sprawdzanie poprawności. Ostatnim razem, gdy sprawdziłem, był to parser Apache Xerces pod przykryciem. Prawdopodobnie powinieneś użyć 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) {}

Stała fabryczna schematu to ciąg znaków, http://www.w3.org/2001/XMLSchemaktóry definiuje XSD. Powyższy kod weryfikuje deskryptor wdrażania WAR względem adresu URLhttp://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd ale równie dobrze można zweryfikować względem pliku lokalnego.

Nie powinieneś używać DOMParser do sprawdzania poprawności dokumentu (chyba że Twoim celem jest utworzenie modelu obiektu dokumentu i tak). Rozpocznie się tworzenie obiektów DOM podczas analizy dokumentu - marnotrawstwo, jeśli nie zamierzasz ich używać.

McDowell
źródło
Czy używasz analizatora składni DOM lub SAX w tym przykładzie? Jak mogę powiedzieć, którego parsera używasz, ponieważ nie mogę zobaczyć odwołania do żadnego z nich.
ziggy
1
@ziggy - jest to szczegół implementacji implementacji JAXP . JDK 6 firmy Sun używa analizatora składni SAX ze StreamSource . W tym przypadku implementacja JAXP mogłaby legalnie używać parsera DOM, ale nie ma żadnego powodu. Jeśli użyjesz jawnie parsera DOM do sprawdzenia poprawności, na pewno utworzysz drzewo DOM.
McDowell,
Jak korzystać z modułu obsługi błędów z powyższym? Czy chodzi tylko o utworzenie ErrorHandlera i skojarzenie go z walidatorem? tj. validator.SetErrorHandler () jak w przykładzie w tym SO SO stackoverflow.com/questions/4864681/... ?
ziggy
Gdyby nie execptions prostu być wykorzystywane w sytuacjach execptional a nie dla przepływu sterowania?
mike
Czy ten kod nie wychwytuje tylko błędów krytycznych? Jeśli chcesz być w stanie złapać nieśmiertelne (takie jak niestrukturalne), myślę, że będziesz musiał użyć ErrorHandlera.
matt forsythe
25

Oto jak to zrobić za pomocą Xerces2 . Samouczek na ten temat tutaj (wymagana rejestracja).

Oryginalne uznanie autorstwa: rażąco skopiowane stąd :

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
źródło
9
Analizator składni SAX byłby bardziej wydajny - analizator DOM tworzy obiekty DOM; marnotrawstwo operacji w tym przypadku.
McDowell,
Pytanie polega na sprawdzeniu poprawności kodu XML względem XSD. W tej odpowiedzi idziesz dalej i otrzymujesz obiekt Parser, który nie jest potrzebny, prawda?
Weslor
„Błąd CheckCheck nie może zostać rozwiązany do typu” .. brakuje importu?
Alex
20

Nasz projekt budujemy za pomocą ant, dzięki czemu możemy użyć zadania schemavalidate do sprawdzenia plików konfiguracyjnych:

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

Teraz niegrzeczne pliki konfiguracyjne zakończą się niepowodzeniem!

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

Ciastko Z Kurczaka
źródło
13

Ponieważ jest to popularne pytanie, zwrócę uwagę, że java może również sprawdzać poprawność względem „odesłanego” xsd, na przykład jeśli sam plik .xml określa XSD w nagłówku, używając xsi:SchemaLocationlub xsi:noNamespaceSchemaLocation(lub xsi dla określonych przestrzeni nazw) np . :

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

lub SchemaLocation (zawsze lista przestrzeni nazw do odwzorowań 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">
  ...

Tutaj działają również inne odpowiedzi, ponieważ pliki .xsd „mapują” na przestrzenie nazw zadeklarowane w pliku .xml, ponieważ deklarują przestrzeń nazw, a jeśli są zgodne z przestrzenią nazw w pliku .xml, jesteś dobry. Ale czasem wygodnie jest mieć niestandardowy program rozpoznawania nazw ...

Z javadocs: „Jeśli utworzysz schemat bez określania adresu URL, pliku lub źródła, wówczas język Java tworzy taki, który sprawdza dokument w trakcie sprawdzania poprawności w celu znalezienia schematu, którego powinien użyć. Na przykład:”

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

i działa to w przypadku wielu przestrzeni nazw itp. Problem z tym podejściem polega na tym, że xmlsns:xsiprawdopodobnie jest to lokalizacja sieciowa, więc domyślnie wychodzi i trafia do sieci przy każdej walidacji, nie zawsze optymalnej.

Oto przykład, który sprawdza poprawność pliku XML względem dowolnych plików XSD, do których się odwołuje (nawet jeśli musi wyciągnąć je z sieci):

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

Można uniknąć ciągnięcia odwołań XSD z sieci, nawet jeśli pliki xml odwołują się do adresów URL, poprzez ręczne określenie xsd (zobacz kilka innych odpowiedzi tutaj) lub za pomocą resolvera w stylu „katalogu XML” . Wygląda na to, że Spring może również przechwytywać żądania adresów URL w celu udostępniania lokalnych plików do weryfikacji. Lub możesz ustawić własne za pomocą setResourceResolver , np .:

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

Zobacz także tutaj, aby zapoznać się z innym samouczkiem.

Wierzę domyślnym jest użycie DOM parsowania, można zrobić coś podobnego z parsera SAX, który jest zatwierdzającego oraz saxReader.setEntityResolver(your_resolver_here);

rogerdpack
źródło
Dla mnie nie działa, metoda resolResource () nie jest wywoływana, chyba że jest ustawiona na schemaFactory, jakiś pomysł?
tomasb
Dunno, działa dla mnie. Upewnij się, że to ustawiasz, setResourceResolverale poza tym, może otwórz nowe pytanie ...
rogerdpack
6

Za pomocą Java 7 możesz postępować zgodnie z dokumentacją zawartą w opisie pakietu .

// 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
źródło
2
„Korzystanie z Java 7 ..” Tak naprawdę było zawarte w Javie 5 .
Andrew Thompson,
4
Jest to w zasadzie to samo co zaakceptowana odpowiedź . To rozwiązanie wydaje mi się choć trochę nieefektywne, gdyż niepotrzebnie buduje Dom na xml do analizowania: parser.parse(new File("instance.xml")). validatorAkceptuje Source, więc można: validator.validate(new StreamSource(new File("instance.xml"))).
Alberto,
W ten sposób wyjątek SAXException byłby zgłaszany przy pierwszym błędzie w pliku xml i zatrzymuje następnie sprawdzanie poprawności. Ale chcę znać wszystkie (!) Błędy. Jeśli zamiast tego użyję ErrorHandler (własna klasa, która implementuje ErrorHandler), rozpoznaje wszystkie błędy, ale try-catch-block of validator.validate nie zgłasza żadnego wyjątku. Jak rozpoznać błąd w klasie, która wywołuje sprawdzanie poprawności - metoda mojego walidatora? Dzięki za pomoc!
mrbela
Występują „błędy” (np. Błędy walidacji) i „błędy krytyczne” (błędy prawidłowej formy). Jeden błąd krytyczny zwykle zatrzymuje parsowanie. Ale błąd sprawdzania poprawności go nie zatrzymuje: musisz jawnie zgłosić wyjątek. Dlatego konieczne jest zapewnienieErrorHandler jeśli konieczne jest sprawdzenie poprawności.
Ludovic Kuty,
1
Muszę przyznać, że kod wygląda na bardziej przejrzysty i łatwiejszy do odczytania niż zaakceptowana odpowiedź.
Mechaniczna
3

Jeśli masz maszynę z systemem Linux, możesz skorzystać z bezpłatnego narzędzia wiersza polecenia SAXCount. Uważam to za bardzo przydatne.

SAXCount -f -s -n my.xml

Sprawdza się względem dtd i xsd. 5s dla pliku 50 MB.

W debian squeeze znajduje się w pakiecie „libxerces-c-samples”.

Definicja dtd i xsd musi znajdować się w xml! Nie można ich konfigurować osobno.

juwens
źródło
2
Pozwala to na proste sprawdzanie poprawności XML z vim (:! SAXCount -f -n -s%)
Shane
4
lub użyj czcigodnego xmllinta xmllint --schema phone.xsd phone.xml(z odpowiedzi 13ren)
rogerdpack,
3

Jeszcze jedna odpowiedź: skoro powiedziałeś, że musisz sprawdzić poprawność generowanych plików (piszesz), możesz chcieć sprawdzić poprawność treści podczas pisania, zamiast najpierw pisać, a potem czytać w celu sprawdzenia poprawności. Prawdopodobnie możesz to zrobić za pomocą JDK API do sprawdzania poprawności Xml, jeśli używasz programu piszącego opartego na SAX: jeśli tak, po prostu połącz w walidatorze, wywołując „Validator.validate (źródło, wynik)”, gdzie źródło pochodzi od twojego pisarza, a wynikiem jest gdzie wyjście musi iść.

Alternatywnie, jeśli używasz Stax do pisania treści (lub biblioteki, która używa lub może używać stax), Woodstox może również bezpośrednio obsługiwać sprawdzanie poprawności przy użyciu XMLStreamWriter. Oto wpis na blogu pokazujący, jak to zrobić:

StaxMan
źródło
Hej, StaxMan, czy są jakieś XMLStreamWriters, które wykonują wcięcia z ładnym drukiem? Byłem zaskoczony, że nie ma go w standardowej implementacji. Czy ma to duże zastosowanie? Myślę, że to właściwa droga, ale wydaje się, że zainteresowanie nią jest niewielkie.
13ren
właśnie znalazłem tutaj swój post o StaxMate (ale nie jest to XMLStreamWriter): stackoverflow.com/questions/290326/stax-xml-formatting-in-java/…
13ren
Tak, StaxMate może to zrobić. Używa XMLStreamWriter wewnętrznie do pisania treści, więc możesz również podłączyć weryfikator w ten sposób.
StaxMan
2

Jeśli programowo generujesz pliki XML, możesz zajrzeć do biblioteki XMLBeans . Za pomocą narzędzia wiersza poleceń XMLBeans automatycznie wygeneruje i spakuje zestaw obiektów Java na podstawie XSD. Następnie można użyć tych obiektów do zbudowania dokumentu XML na podstawie tego schematu.

Ma wbudowaną obsługę sprawdzania poprawności schematu i może konwertować obiekty Java na dokument XML i odwrotnie.

Castor i JAXB to inne biblioteki Java, które służą podobnym celom jak XMLBeans.

Todd
źródło
1

Dzięki JAXB możesz użyć poniższego kodu:

    @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
źródło
0

Szukasz narzędzia lub biblioteki?

Jeśli chodzi o biblioteki, de facto standardem jest Xerces2, który ma zarówno wersje C ++, jak i Java .

Ostrzegam jednak, że jest to rozwiązanie ciężkie. Ale z drugiej strony sprawdzanie poprawności XML względem plików XSD jest dość dużym problemem.

Jeśli chodzi o narzędzie do zrobienia tego dla ciebie, XMLFox wydaje się być przyzwoitym darmowym rozwiązaniem, ale nie użyłem go osobiście, nie mogę powiedzieć na pewno.

Adam
źródło
0

Sprawdź poprawność pod kątem schematów 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);

Sprawdź poprawność względem lokalnych schematów

Offline XML Validation with Java

jschnasse
źródło
0

Za pomocą Woodstox skonfiguruj analizator składni StAX, aby sprawdzić poprawność względem schematu i przeanalizuj XML.

Jeśli wychwycone zostaną wyjątki, kod XML jest niepoprawny, w przeciwnym razie jest poprawny:

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

Uwaga : jeśli musisz sprawdzić poprawność wielu plików, powinieneś spróbować ponownie użyć swojego XMLInputFactoryi XMLValidationSchema, aby zmaksymalizować wydajność.

Loris Securo
źródło
-3

Musiałem tylko raz sprawdzić XML względem XSD, więc wypróbowałem XMLFox. Uważam, że jest to bardzo mylące i dziwne. Wydawało się, że instrukcje pomocy nie pasują do interfejsu.

Skończyło się na użyciu LiquidXML Studio 2008 (v6), które było znacznie łatwiejsze w użyciu i od razu bardziej znane (interfejs użytkownika jest bardzo podobny do Visual Basic 2008 Express, z którego często korzystam). Wada: funkcja sprawdzania poprawności nie jest dostępna w wersji bezpłatnej, więc musiałem skorzystać z 30-dniowej wersji próbnej.

KnomDeGuerre
źródło
1
Pytanie brzmi Java, ale nie jest to odpowiedź. :-(
james.garriss
Szczerze mówiąc, słowo „java” nigdy nie pojawia się w pytaniu, tylko w tagach. Zadałbym pytanie, a nie odpowiedź.
Mark Storer,
Dzięki James i Mark, pomóżcie mi wyostrzyć!
Knom,