Cách tốt nhất để xác thực tệp XML đối với tệp XSD là gì?


263

Tôi đang tạo một số tệp xml cần tuân thủ tệp xsd đã được cung cấp cho tôi. Cách tốt nhất để xác minh họ phù hợp là gì?

Câu trả lời:


336

Thư viện thời gian chạy Java hỗ trợ xác nhận hợp lệ. Lần trước tôi đã kiểm tra đây là trình phân tích cú pháp Apache Xerces dưới vỏ bọc. Có lẽ bạn nên sử dụng một 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) {}

Hằng số nhà máy lược đồ là chuỗi http://www.w3.org/2001/XMLSchemaxác định XSD. Đoạn mã trên xác nhận một mô tả triển khai WAR dựa trên URL http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsdnhưng bạn có thể dễ dàng xác nhận đối với một tệp cục bộ.

Bạn không nên sử dụng DOMParser để xác thực tài liệu (trừ khi mục tiêu của bạn là tạo mô hình đối tượng tài liệu bằng mọi cách). Điều này sẽ bắt đầu tạo các đối tượng DOM vì nó phân tích tài liệu - thật lãng phí nếu bạn không sử dụng chúng.


Bạn có đang sử dụng trình phân tích cú pháp DOM hoặc SAX trong ví dụ này không? Làm thế nào để tôi biết bạn đang sử dụng trình phân tích cú pháp nào vì tôi không thể thấy một tài liệu tham khảo.
ngoằn ngoèo

1
@ziggy - đây là chi tiết triển khai thực hiện JAXP . JDK 6 của Sun sử dụng trình phân tích cú pháp SAX với StreamSource . Việc triển khai JAXP có thể sử dụng hợp pháp trình phân tích cú pháp DOM trong trường hợp này, nhưng không có lý do gì để làm. Nếu bạn sử dụng một trình phân tích cú pháp DOM rõ ràng để xác thực, bạn chắc chắn sẽ khởi tạo một cây DOM.
McDowell

Làm cách nào để sử dụng ErrorHandler với phần trên? Đây có phải là trường hợp chỉ tạo ErrorHandler và liên kết nó với trình xác nhận hợp lệ không? tức là trình xác nhận.SetErrorHandler () như trong ví dụ trong câu hỏi SO stackoverflow.com/questions/4864681/ này ?
ngoằn ngoèo

Không nên thực thi chỉ được sử dụng cho các tình huống thực thi và không cho luồng điều khiển?
mike

Không phải mã này chỉ bắt lỗi nghiêm trọng? Nếu bạn muốn có thể bắt những người không gây tử vong (chẳng hạn như những người không có cấu trúc) Tôi nghĩ bạn sẽ cần phải sử dụng ErrorHandler.
matt forsythe

25

Đây là cách thực hiện bằng Xerces2 . Một hướng dẫn cho điều này, ở đây (đăng ký lại).

Ghi công gốc: sao chép trắng trợn từ đây :

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
Trình phân tích cú pháp SAX sẽ hiệu quả hơn - trình phân tích cú pháp DOM tạo các đối tượng DOM; hoạt động lãng phí trong trường hợp này.
McDowell

Câu hỏi là xác nhận XML dựa trên XSD. Trong câu trả lời này, bạn sẽ đi xa hơn và nhận được một đối tượng Parser, không cần thiết, phải không?
Weslor

"ErrorChecker cannor được giải quyết thành một loại" .. thiếu nhập?
Alex

20

Chúng tôi xây dựng dự án của mình bằng ant, vì vậy chúng tôi có thể sử dụng tác vụ schemavalidate để kiểm tra các tệp cấu hình của mình:

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

Bây giờ các tập tin cấu hình nghịch ngợm sẽ thất bại trong quá trình xây dựng của chúng tôi!

http://ant.apache.org/manual/T task / schemavalidate.html


13

Vì đây là một câu hỏi phổ biến, tôi sẽ chỉ ra rằng java cũng có thể xác nhận hợp lệ đối với "được đề cập" của xsd, chẳng hạn nếu chính tệp .xml chỉ định XSD trong tiêu đề, sử dụng xsi:SchemaLocationhoặc xsi:noNamespaceSchemaLocation(hoặc xsi cho các không gian tên cụ thể) ex :

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

hoặc SchemaLocation (luôn là danh sách không gian tên cho ánh xạ 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">
  ...

Các câu trả lời khác cũng hoạt động ở đây, vì các tệp .xsd "ánh xạ" tới các không gian tên được khai báo trong tệp .xml, vì chúng khai báo một không gian tên và nếu khớp với không gian tên trong tệp .xml, thì tốt. Nhưng đôi khi thật tiện lợi khi có thể có trình phân giải tùy chỉnh ...

Từ javadocs: "Nếu bạn tạo một lược đồ mà không chỉ định URL, tệp hoặc nguồn, thì ngôn ngữ Java sẽ tạo một lược đồ trong tài liệu đang được xác thực để tìm lược đồ mà nó nên sử dụng. Ví dụ:"

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

và điều này hoạt động cho nhiều không gian tên, v.v. Vấn đề với cách tiếp cận này xmlsns:xsilà có lẽ là một vị trí mạng, do đó, mặc định nó sẽ đi ra ngoài và truy cập mạng với mỗi xác thực, không phải lúc nào cũng tối ưu.

Đây là một ví dụ xác thực một tệp XML dựa trên bất kỳ XSD nào mà nó tham chiếu (ngay cả khi nó phải kéo chúng ra khỏi mạng):

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

Bạn có thể tránh kéo các XSD được tham chiếu khỏi mạng, mặc dù các url tham chiếu tệp xml, bằng cách chỉ định xsd theo cách thủ công (xem một số câu trả lời khác tại đây) hoặc bằng cách sử dụng trình phân giải kiểu "danh mục XML" . Spring rõ ràng cũng có thể chặn các yêu cầu URL để phục vụ các tệp cục bộ để xác thực. Hoặc bạn có thể đặt riêng của mình thông qua setResourceResolver , ví dụ:

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

Xem thêm ở đây để xem hướng dẫn khác.

Tôi tin mặc định là sử dụng DOM phân tích cú pháp, bạn có thể làm điều gì đó tương tự với SAX phân tích cú pháp được xác nhận cũng saxReader.setEntityResolver(your_resolver_here);


Không hoạt động đối với tôi, phương thức notifyResource () không được gọi trừ khi được đặt trên lược đồFactory, có ý tưởng nào không?
tomasb

Dunno, làm việc cho tôi. Hãy chắc chắn rằng bạn đang đặt nó qua setResourceResolvernhưng ngoài đó, có thể mở câu hỏi mới ...
rogerdpack

6

Sử dụng Java 7, bạn có thể làm theo tài liệu được cung cấp trong mô tả gói .

// 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
"Sử dụng Java 7 .." Điều đó thực sự được bao gồm trong Java 5 .
Andrew Thompson

4
Điều này về cơ bản giống như câu trả lời được chấp nhận . Tuy nhiên, giải pháp này có vẻ hơi kém hiệu quả, vì nó không cần thiết xây dựng DOM cho xml để phân tích : parser.parse(new File("instance.xml")). Việc validatorchấp nhận a Source, vì vậy bạn có thể : validator.validate(new StreamSource(new File("instance.xml"))).
Alberto

Làm việc theo cách này, SAXException sẽ bị ném ở lỗi đầu tiên trong tệp xml và dừng sau đó xác thực. Nhưng tôi muốn biết tất cả (!) Lỗi. Thay vào đó, nếu tôi sử dụng ErrorHandler (lớp riêng thực hiện ErrorHandler), nó sẽ nhận ra tất cả các lỗi, nhưng khối try-Catch-block của validator.validate không đưa ra bất kỳ Ngoại lệ nào .. Làm cách nào để nhận ra lỗi trong lớp gọi -method của trình xác nhận của tôi? Cảm ơn bạn đã giúp đỡ!
mrbela

Có "lỗi" (ví dụ: lỗi xác nhận) và "lỗi nghiêm trọng" (lỗi định dạng tốt). Một lỗi nghiêm trọng thường dừng phân tích cú pháp. Nhưng một lỗi xác nhận không ngăn được nó: bạn phải ném một ngoại lệ rõ ràng. Vì vậy, cần phải cung cấp ErrorHandlernếu bạn cần xác thực.
Ludovic Kuty

1
Phải thừa nhận, mã trông sạch hơn và dễ đọc hơn câu trả lời được chấp nhận.
Đồng hồ

3

Nếu bạn có Linux-Machine, bạn có thể sử dụng công cụ dòng lệnh miễn phí SAXCount. Tôi thấy điều này rất hữu ích.

SAXCount -f -s -n my.xml

Nó xác nhận chống lại dtd và xsd. 5s cho tệp 50MB.

Trong debian bóp nó nằm trong gói "libxerces-c-samples".

Định nghĩa của dtd và xsd phải có trong xml! Bạn không thể cấu hình chúng một cách riêng biệt.


2
Điều này cho phép xác thực XML đơn giản từ vim (:! SAXCount -f -n -s%)
Shane

4
hoặc sử dụng xmllint đáng kính xmllint --schema phone.xsd phone.xml(từ câu trả lời của 13ren)
rogerdpack

3

Thêm một câu trả lời: vì bạn nói rằng bạn cần xác thực các tệp bạn đang tạo (viết), bạn có thể muốn xác thực nội dung trong khi bạn đang viết, thay vì viết trước, sau đó đọc lại để xác thực. Bạn có thể có thể làm điều đó với xác thực API JDK để xác thực Xml, nếu bạn sử dụng trình soạn thảo dựa trên SAX: nếu vậy, chỉ cần liên kết trong trình xác thực bằng cách gọi 'Validator.validate (nguồn, kết quả)', trong đó nguồn đến từ nhà văn của bạn và kết quả là nơi đầu ra cần phải đi.

Ngoài ra, nếu bạn sử dụng Stax để viết nội dung (hoặc thư viện sử dụng hoặc có thể sử dụng stax), Woodstox cũng có thể hỗ trợ trực tiếp xác thực khi sử dụng XMLStreamWriter. Đây là một mục blog cho thấy làm thế nào được thực hiện:


Xin chào StaxMan, có bất kỳ XMLStreamWriters nào thụt lề in ấn đẹp không? Tôi đã ngạc nhiên rằng nó không nằm trong tiêu chuẩn thực hiện. Ngoài ra, nó có được sử dụng nhiều không? Tôi nghĩ rằng đó là cách đúng đắn để đi, nhưng dường như rất ít quan tâm đến nó.
13ren

vừa tìm thấy bài đăng của bạn ở đây về StaxMate (nhưng nó không phải là XMLStreamWriter): stackoverflow.com/questions/290326/stax-xml-formatted-in-java/iêu
13ren

Vâng, StaxMate có thể làm điều đó. Nó sử dụng XMLStreamWriter trong nội bộ để viết nội dung, do đó bạn cũng có thể kết nối trình xác nhận theo cách đó.
StaxMan

2

Nếu bạn đang tạo các tệp XML theo chương trình, bạn có thể muốn xem thư viện XMLBeans . Sử dụng một công cụ dòng lệnh, XMLBeans sẽ tự động tạo và đóng gói một tập hợp các đối tượng Java dựa trên XSD. Sau đó, bạn có thể sử dụng các đối tượng này để xây dựng một tài liệu XML dựa trên lược đồ này.

Nó có hỗ trợ tích hợp để xác thực lược đồ và có thể chuyển đổi các đối tượng Java thành tài liệu XML và ngược lại.

CastorJAXB là các thư viện Java khác phục vụ mục đích tương tự như XMLBeans.


1

Với JAXB, bạn có thể sử dụng mã dưới đây:

    @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

Bạn đang tìm kiếm một công cụ hoặc một thư viện?

Theo như các thư viện, khá nhiều tiêu chuẩn thực tế là Xerces2 có cả phiên bản C ++Java .

Được cảnh báo trước mặc dù, nó là một giải pháp trọng lượng nặng. Nhưng một lần nữa, việc xác thực XML đối với các tệp XSD là một vấn đề khá nặng nề.

Đối với một công cụ để làm điều này cho bạn, XMLFox dường như là một giải pháp phần mềm miễn phí hợp lý, nhưng tôi không chắc chắn sử dụng nó.


0

Xác thực đối với các lược đồ trực tuyến

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

Xác thực đối với các lược đồ địa phương

Xác thực XML ngoại tuyến với Java


0

Sử dụng Woodstox , định cấu hình trình phân tích cú pháp StAX để xác thực theo lược đồ của bạn và phân tích cú pháp XML.

Nếu ngoại lệ bị bắt thì XML không hợp lệ, nếu không thì nó hợp lệ:

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

Lưu ý : Nếu bạn cần xác thực nhiều tệp, bạn nên cố gắng sử dụng lại XMLInputFactoryXMLValidationSchemađể tối đa hóa hiệu suất.


-3

Tôi đã phải xác thực một XML dựa trên XSD chỉ một lần, vì vậy tôi đã thử XMLFox. Tôi thấy nó rất khó hiểu và kỳ lạ. Các hướng dẫn trợ giúp dường như không khớp với giao diện.

Tôi đã kết thúc việc sử dụng LiquidXML Studio 2008 (v6), nó dễ sử dụng hơn và quen thuộc hơn ngay lập tức (giao diện người dùng rất giống với Visual Basic 2008 Express, mà tôi thường sử dụng). Hạn chế: khả năng xác nhận không có trong phiên bản miễn phí, vì vậy tôi đã phải sử dụng bản dùng thử 30 ngày.


1
Câu hỏi là Java, nhưng câu trả lời này thì không. :-(
james.garriss 7/10/2015

Công bằng mà nói, từ "java" không bao giờ xuất hiện trong câu hỏi, chỉ là các thẻ. Tôi đã đặt câu hỏi cho điều đó, không phải là câu trả lời.
Mark Storer

Cảm ơn james và Mark, giúp tôi làm sắc nét lên!
Knom
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.