Проверка файла XML с использованием нескольких схемных определений
Я пытаюсь проверить XML файл на несколько разных схем (извинения за надуманный пример):
c.xsd в частности импортирует b.xsd и b.xsd импортирует a.xsd, используя:
<xs:include schemaLocation="b.xsd"/>
Я пытаюсь сделать это через Xerces следующим образом:
XMLSchemaFactory xmlSchemaFactory = new XMLSchemaFactory();
Schema schema = xmlSchemaFactory.newSchema(new StreamSource[] { new StreamSource(this.getClass().getResourceAsStream("a.xsd"), "a.xsd"),
new StreamSource(this.getClass().getResourceAsStream("b.xsd"), "b.xsd"),
new StreamSource(this.getClass().getResourceAsStream("c.xsd"), "c.xsd")});
Validator validator = schema.newValidator();
validator.validate(new StreamSource(new StringReader(xmlContent)));
но это не позволяет импортировать все три схемы правильно, в результате чего невозможно разрешить имя "бла" для компонента (n) "group".
Я успешно проверил это с помощью Python, но имел реальные проблемы с Java 6.0 и Xerces 2.8.1. Кто-нибудь может предположить, что здесь происходит, или более простой подход для проверки моих XML-документов?
Ответы
Ответ 1
Таким образом, на всякий случай кто-то другой сталкивается с той же проблемой здесь, мне нужно было загрузить родительскую схему (и неявные дочерние схемы) из unit test - в качестве ресурса - для проверки XML-строки. Я использовал Xerces XMLSchemFactory для этого вместе с валидатором Java 6.
Чтобы правильно загрузить дочернюю схему с помощью include, мне пришлось написать настраиваемый распознаватель ресурсов. Код можно найти здесь:
https://code.google.com/p/xmlsanity/source/browse/src/com/arc90/xmlsanity/validation/ResourceResolver.java
Чтобы использовать распознаватель, укажите его в схеме factory:
xmlSchemaFactory.setResourceResolver(new ResourceResolver());
и он будет использовать его для разрешения ваших ресурсов через путь к классам (в моем случае из src/main/resources). Любые комментарии приветствуются в этом...
Ответ 2
http://www.kdgregory.com/index.php?page=xml.parsing
раздел ' Несколько схем для одного документа
Мое решение на основе этого документа:
URL xsdUrlA = this.getClass().getResource("a.xsd");
URL xsdUrlB = this.getClass().getResource("b.xsd");
URL xsdUrlC = this.getClass().getResource("c.xsd");
SchemaFactory schemaFactory = schemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
//---
String W3C_XSD_TOP_ELEMENT =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" elementFormDefault=\"qualified\">\n"
+ "<xs:include schemaLocation=\"" +xsdUrlA.getPath() +"\"/>\n"
+ "<xs:include schemaLocation=\"" +xsdUrlB.getPath() +"\"/>\n"
+ "<xs:include schemaLocation=\"" +xsdUrlC.getPath() +"\"/>\n"
+"</xs:schema>";
Schema schema = schemaFactory.newSchema(new StreamSource(new StringReader(W3C_XSD_TOP_ELEMENT), "xsdTop"));
Ответ 3
Материал схемы в Xerces (a) очень, очень педантичен и (b) дает совершенно бесполезные сообщения об ошибках, когда ему не нравится то, что он находит. Это разочаровывающая комбинация.
Компиляция схемы в python может быть намного более прощающей и позволяла небольшим ошибкам в схеме проходить мимо незарегистрированных.
Теперь, если, как вы говорите, c.xsd включает b.xsd, а b.xsd включает a.xsd, тогда нет необходимости загружать все три в схему factory. Мало того, что это необязательно, скорее всего, это путает Xerces и приведет к ошибкам, так что это может быть вашей проблемой. Просто передайте c.xsd в factory и разрешите ему решить b.xsd и сам a.xsd, что он должен делать относительно c.xsd.
Ответ 4
Из документации xerces:
http://xerces.apache.org/xerces2-j/faq-xs.html
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
...
StreamSource[] schemaDocuments = /* created by your application */;
Source instanceDocument = /* created by your application */;
SchemaFactory sf = SchemaFactory.newInstance(
"http://www.w3.org/XML/XMLSchema/v1.1");
Schema s = sf.newSchema(schemaDocuments);
Validator v = s.newValidator();
v.validate(instanceDocument);
Ответ 5
В итоге я использовал это:
import org.apache.xerces.parsers.SAXParser;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import java.io.IOException;
.
.
.
try {
SAXParser parser = new SAXParser();
parser.setFeature("http://xml.org/sax/features/validation", true);
parser.setFeature("http://apache.org/xml/features/validation/schema", true);
parser.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true);
parser.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", "http://your_url_schema_location");
Validator handler = new Validator();
parser.setErrorHandler(handler);
parser.parse("file:///" + "/home/user/myfile.xml");
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException ex) {
e.printStackTrace();
}
class Validator extends DefaultHandler {
public boolean validationError = false;
public SAXParseException saxParseException = null;
public void error(SAXParseException exception)
throws SAXException {
validationError = true;
saxParseException = exception;
}
public void fatalError(SAXParseException exception)
throws SAXException {
validationError = true;
saxParseException = exception;
}
public void warning(SAXParseException exception)
throws SAXException {
}
}
Не забудьте изменить:
1) Параметр "http://your_url_schema_location" для размещения файла xsd.
2) Строка "/home/user/myfile.xml" для указателя на ваш XML файл.
Мне не нужно было устанавливать переменную: -Djavax.xml.validation.SchemaFactory:http://www.w3.org/2001/XMLSchema=org.apache.xerces.jaxp.validation.XMLSchemaFactory
Ответ 6
Я столкнулся с той же проблемой и после исследования нашел это решение. Это работает для меня.
Enum
, чтобы настроить различные XSDs
:
public enum XsdFile {
// @formatter:off
A("a.xsd"),
B("b.xsd"),
C("c.xsd");
// @formatter:on
private final String value;
private XsdFile(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
}
Способ проверки:
public static void validateXmlAgainstManyXsds() {
final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
String xmlFile;
xmlFile = "example.xml";
// Use of Enum class in order to get the different XSDs
Source[] sources = new Source[XsdFile.class.getEnumConstants().length];
for (XsdFile xsdFile : XsdFile.class.getEnumConstants()) {
sources[xsdFile.ordinal()] = new StreamSource(xsdFile.getValue());
}
try {
final Schema schema = schemaFactory.newSchema(sources);
final Validator validator = schema.newValidator();
System.out.println("Validating " + xmlFile + " against XSDs " + Arrays.toString(sources));
validator.validate(new StreamSource(new File(xmlFile)));
} catch (Exception exception) {
System.out.println("ERROR: Unable to validate " + xmlFile + " against XSDs " + Arrays.toString(sources)
+ " - " + exception);
}
System.out.println("Validation process completed.");
}