Bir XML dosyasını bir XSD dosyasına karşı doğrulamanın en iyi yolu nedir?


263

Bana verilen bir xsd dosyasına uyması gereken bazı xml dosyaları üretiyorum. Uygun olduklarını doğrulamanın en iyi yolu nedir?

Yanıtlar:


336

Java çalışma zamanı kitaplığı doğrulamayı destekler. Son kontrol ettiğimde kapakların altında Apache Xerces ayrıştırıcısı vardı. Muhtemelen bir javax.xml.validation.Validator kullanmalısınız .

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

Şema fabrika sabiti, http://www.w3.org/2001/XMLSchemaXSD'leri tanımlayan dizedir . Yukarıdaki kod, bir WAR dağıtım tanımlayıcısını URL'ye göre http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsddoğrular, ancak yerel bir dosyaya karşı kolayca doğrulama yapabilirsiniz.

DOMParser'ı bir belgeyi doğrulamak için kullanmamalısınız (hedefiniz zaten bir belge nesnesi modeli oluşturmak değilse). Bu, belgeyi ayrıştırdığı için DOM nesneleri oluşturmaya başlayacaktır - bunları kullanmayacaksanız savurgan olur.


Bu örnekte bir DOM veya SAX ayrıştırıcısı mı kullanıyorsunuz? Nasıl bir başvuru göremiyorum olarak hangi ayrıştırıcı kullandığınızı nasıl anlatacağım.
ziggy

1
@ziggy - Bu JAXP uygulamasının bir uygulama detayıdır . Sun'ın JDK 6 bir StreamSource ile SAX ayrıştırıcı kullanır . Bir JAXP uygulama olabilir yasal olarak bu durumda bir DOM ayrıştırıcı kullanın, ancak hiçbir neden yoktur. Eğer varsa bir DOM ayrıştırıcı kullanmak doğrulama için açıkça, kesinlikle bir DOM ağacı örneğini.
McDowell

Yukarıdakilerle birlikte bir ErrorHandler'ı nasıl kullanabilirim? Sadece ErrorHandler'ı oluşturmak ve doğrulayıcı ile ilişkilendirmek mi? yani bu SO sorusundaki örnekteki gibi validator.SetErrorHandler () stackoverflow.com/questions/4864681/… ?
ziggy

Uygulamalar kontrol akışı için değil , yalnızca yürütme durumları için kullanılmamalı mıdır?
mike

Bu kod sadece önemli hataları yakalamayacak mı? Eğer ölümcül olmayanları (yapısal olmayanlar gibi) yakalamak istiyorsanız bir ErrorHandler kullanmanız gerektiğini düşünüyorum.
mat forsythe

25

Xerces2'yi kullanarak bunu nasıl yapacağınız aşağıda açıklanmıştır . Bunun için bir öğretici, burada (gerekli kayıt).

Orijinal atıf: buradan açıkça kopyalandı :

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 ayrıştırıcısı daha verimli olur - DOM ayrıştırıcısı DOM nesneleri oluşturur; bu durumda savurgan işlemler.
McDowell

Soru, bir XML'i bir XSD'ye göre doğrulamaktır. Bu cevapta daha ileri gidiyorsunuz ve gerekli olmayan bir Parser nesnesi alıyorsunuz, değil mi?
Weslor

"ErrorChecker bir tür çözümlenebilir" .. eksik alma?
Alex

20

Projemizi karınca kullanarak oluşturuyoruz, böylece yapılandırma dosyalarımızı kontrol etmek için schemavalidate görevini kullanabiliriz:

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

Şimdi yaramaz yapılandırma dosyaları bizim yapı başarısız olacak!

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


13

Bu popüler bir sorudur bu yana .xml dosyası kendisi XSD en kullanarak, başlıkta belirtirse, ben de validate karşı örneğin Xsd yönettiği "atıfta" anlamına java işaret eder xsi:SchemaLocationya da xsi:noNamespaceSchemaLocation(ya da belirli ad alanları için xsi) ex :

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

veya SchemaLocation (her zaman xsd eşlemeleri için bir ad alanı listesi)

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

Diğer yanıtlar da burada çalışır, çünkü .xsd dosyaları .xml dosyasında bildirilen ad alanlarıyla "eşleşir", çünkü bir ad alanı bildirir ve .xml dosyasındaki ad alanıyla eşleşirse, iyidir. Ancak bazen özel bir çözümleyiciye sahip olmak uygun olur ...

Javadocs'tan: "Bir URL, dosya veya kaynak belirtmeden bir şema oluşturursanız, Java dili, kullanması gereken şemayı bulmak için onaylanan belgede görünen bir şema oluşturur. Örneğin:"

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

ve bu, birden çok ad alanı, vb. için çalışır. Bu yaklaşımla ilgili sorun xmlsns:xsi, muhtemelen bir ağ konumu olmasıdır, bu nedenle varsayılan olarak dışarı çıkıp her zaman her doğrulamayla ağa çarpar, her zaman en uygun değildir.

XML dosyasını başvurduğu herhangi bir XSD'ye göre doğrulayan bir örnek (ağdan çekmek zorunda olsa bile):

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

Xml dosyaları url'lere başvursa da, xsd'yi manuel olarak belirterek (burada diğer yanıtlara bakın) veya "XML kataloğu" stil çözümleyici kullanarak başvurulan XSD'leri ağdan çekmekten kaçınabilirsiniz . Spring, görünüşe göre doğrulama için yerel dosyalara sunmak için URL isteklerini de engelleyebilir . Veya setResourceResolver aracılığıyla kendiniz ayarlayabilirsiniz , örn:

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

Başka bir eğitim için buraya da bakınız .

Ben, sen doğruluyor SAX ayrıştırıcı ile benzer bir şey yapabilirsiniz varsayılan DOM ayrıştırmayı kullanmaktır inanıyoruz yanı saxReader.setEntityResolver(your_resolver_here);


Benim için çalışmıyor, schemaFactory üzerinde ayarlanmadığı sürece, solutionResResource () yöntemi çağrılmıyor mu?
tomasb

Dunno, benim için çalışıyor. Bunu ayarladığınızdan emin olun, setResourceResolverancak bunun ötesinde, belki de yeni bir soru açın ...
rogerdpack

6

Java 7'yi kullanarak paket açıklamasında sağlanan belgeleri takip edebilirsiniz .

// 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 Kullanılıyor .." Bu aslında Java 5'e dahil edildi .
Andrew Thompson

4
Bu temel olarak kabul edilen cevapla aynıdır . O gereksiz yere ayrıştırma için xml için DOM kurar gibi bu çözüm, bana biraz verimsiz karşın, yine de görünüyor: parser.parse(new File("instance.xml")). validatorBir kabul Source, böylece şunları yapabilirsiniz: validator.validate(new StreamSource(new File("instance.xml"))).
Alberto

Bu şekilde çalışırken, xml dosyasındaki ilk hatada bir SAXException atılır ve ardından doğrulama durur. Ama tüm (!) Hataları bilmek istiyorum. Bunun yerine bir ErrorHandler (ErrorHandler uygulayan kendi sınıfı) kullanırsanız, tüm hataları tanır, ancak validator.validate try-catch-bloğu herhangi bir İstisna atmaz .. Doğrulamayı çağıran sınıftaki bir hatayı nasıl tanıyabilirim? - validatorumun yöntemi? Yardımınız için teşekkürler!
mrbela

"Hatalar" (örn. Doğrulama hataları) ve "ölümcül hatalar" (iyi biçimlilik hataları) vardır. Önemli bir hata genellikle ayrıştırmayı durdurur. Ancak bir doğrulama hatası bunu durdurmaz: açıkça bir istisna atmanız gerekir. Bu nedenle, ErrorHandlerdoğrulama yapmanız gerekiyorsa bir sağlamak gerekir.
Ludovic Kuty

1
İtiraf etmeliyim ki, kod kabul edilen cevaptan daha temiz ve okunması daha kolay görünüyor.
Clockwork

3

Bir Linux Makineniz varsa, ücretsiz komut satırı aracı SAXCount'u kullanabilirsiniz. Bunu çok faydalı buldum.

SAXCount -f -s -n my.xml

Dtd ve xsd'ye göre geçer. 50MB dosya için 5s.

Debian squeeze "libxerces-c-samples" paketinde bulunur.

Dtd ve xsd tanımı xml olmalıdır! Bunları ayrı ayrı yapılandıramazsınız.


2
Bu, vim'den basit XML doğrulamasına izin verir (:! SAXCount -f -n -s%)
Shane

4
ya da saygıdeğer xmllint kullanın xmllint --schema phone.xsd phone.xml(13ren cevap)
rogerdpack

3

Bir daha cevap: Eğer validate Sizin dosyaları ihtiyacım olduğunu söyledi beri üreten (yazma), sonra yerine ilk yazı yazma doğrulama için geri okurken, sen doğrulamak içeriği isteyebilirsiniz. Bunu muhtemelen Xml doğrulaması için JDK API ile yapabilirsiniz, eğer SAX tabanlı bir yazar kullanıyorsanız: eğer öyleyse, kaynaktan yazarınızdan gelen ve sonuçların olduğu 'Validator.validate (kaynak, sonuç)' öğesini çağırarak doğrulayıcıya bağlanmanız yeterlidir. çıktının gitmesi gereken yer.

Alternatif olarak, içerik yazmak için Stax kullanıyorsanız (veya stax kullanan veya kullanabilen bir kütüphane), Woodstox XMLStreamWriter kullanırken doğrulamayı doğrudan destekleyebilir. İşte bunun nasıl yapıldığını gösteren bir blog girişi :


Hey StaxMan, güzel baskı girintisi yapan herhangi bir XMLStreamWriters var mı? Standart uygulamada olmadığına şaşırdım. Ayrıca, çok faydalanıyor mu? Bence bu doğru bir yol ama çok az ilgi var.
13ren

StaxMate hakkındaki yazınızı burada buldunuz (ancak bu bir XMLStreamWriter değil): stackoverflow.com/questions/290326/stax-xml-formatting-in-java/…
13ren

Evet, StaxMate bunu yapabilir. İçerik yazmak için dahili olarak XMLStreamWriter kullanır, böylece doğrulayıcıyı bu şekilde de bağlayabilirsiniz.
StaxMan

2

XML dosyalarını programlı olarak oluşturuyorsanız, XMLBeans kütüphanesine bakmak isteyebilirsiniz . Bir komut satırı aracı kullanarak, XMLBeans otomatik olarak bir XSD tabanlı bir dizi Java nesnesi oluşturur ve paketler. Daha sonra bu nesneleri, bu şemaya dayalı bir XML belgesi oluşturmak için kullanabilirsiniz.

Şema doğrulaması için yerleşik bir desteğe sahiptir ve Java nesnelerini bir XML belgesine veya tersine dönüştürebilir.

Castor ve JAXB , XMLBeans'e benzer bir amaca hizmet eden diğer Java kütüphaneleridir.


1

JAXB ile aşağıdaki kodu kullanabilirsiniz:

    @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

Bir araç veya kütüphane mi arıyorsunuz?

Bildiğim kadarıyla kütüphaneler giderse, hemen hemen de-facto standart Xerces2 hem sahiptir C ++ ve Java sürümleri.

Dikkat edin, ağır bir çözümdür. Fakat yine de, XML'i XSD dosyalarına karşı doğrulamak oldukça ağır bir sorundur.

Bunu sizin için yapacak bir araca gelince, XMLFox iyi bir ücretsiz çözüm gibi görünüyor, ancak kişisel olarak kullanmadım emin diyemem.


0

Çevrimiçi şemalara karşı doğrulama

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

Yerel şemalara karşı doğrulama

Java ile Çevrimdışı XML Doğrulaması


0

Woodstox kullanarak , StAX ayrıştırıcısını şemanıza göre doğrulayacak ve XML'yi ayrıştıracak şekilde yapılandırın.

İstisnalar yakalanırsa XML geçerli değildir, aksi takdirde geçerlidir:

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

Not : Birden fazla dosyayı doğrulamanız gerekiyorsa , performansınızı en üst düzeye çıkarmak için XMLInputFactoryve dosyalarınızı yeniden kullanmaya çalışmalısınız XMLValidationSchema.


-3

Ben sadece bir kez XSD bir XML doğrulamak zorunda kaldı, bu yüzden XMLFox denedim. Çok kafa karıştırıcı ve garip buldum. Yardım talimatları arayüzle eşleşmiyor gibi görünüyor.

Kullanımı çok daha kolay ve hemen tanıdık olan LiquidXML Studio 2008'i (v6) kullandım (kullanıcı arayüzü sık kullandığım Visual Basic 2008 Express'e çok benziyor). Dezavantajı: doğrulama yeteneği ücretsiz sürümünde değil, bu yüzden 30 günlük deneme kullanmak zorunda kaldı.


1
Soru Java, ancak bu cevap değil. :-(
james.garriss

Adil olmak gerekirse, soruda "java" kelimesi asla görünmez, yalnızca etiketler. Bunun için soruyu söylerdim, cevap değil.
Mark Storer

Teşekkürler james ve Mark, keskinleştirmeme yardım et!
14'te Knom
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.