XSD スキーマに対して XML ファイルを検証する必要があります。スキーマは、(include および import ディレクティブを使用して) 複数の XSD ファイルに分散されます。
SO の投稿で既に見つけた質問/回答に従うことで、次の解決策を思いつきました。
(次のコードは急速に開発されたプロトタイプであり、最終的な解決策ではないことに注意してください。)
private static final String PROJECT_ROOT_DIR_PATH = "--- project-root-path ---";
private static final String SCHEMAS_ROOT_PATH = "--- schemas root path ---";
private void validate() throws Exception
{
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
DocumentBuilder parser = builderFactory.newDocumentBuilder();
// parse the XML into a document object
Document document = parser.parse(
new File(
PROJECT_ROOT_DIR_PATH +
"src\\test\\resources\\example.xml"
)
);
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// associate the schema factory with the resource resolver, which is responsible for resolving the imported XSD's
factory.setResourceResolver(new ResourceResolver(PROJECT_ROOT_DIR_PATH + SCHEMAS_ROOT_PATH));
Schema schema = factory.newSchema(
new File(
PROJECT_ROOT_DIR_PATH +
SCHEMAS_ROOT_PATH +
"--- root-xsd-file-path\\root-schema.xsd ---"
)
);
Validator validator = schema.newValidator();
validator.validate(new DOMSource(document));
}
ResourceResolver.java:
public class ResourceResolver implements LSResourceResolver
{
@Override
public LSInput resolveResource(String type, String namespaceURI,
String publicId, String systemId, String baseURI)
{
if (!"http://www.w3.org/2001/XMLSchema".equals(type))
{
throw new IllegalArgumentException(
"Unexpected resource type [" + type + "]."
);
}
if (systemId == null)
{
throw new IllegalArgumentException(
"Unexpected resource system-id [" + systemId + "]."
);
}
System.out.println("base-uri: " + baseURI);
System.out.println("system-id: " + systemId);
URI targetURI = getTargetURI(baseURI, systemId);
System.out.println("target-uri: " + targetURI);
System.out.println("---");
Input input = null;
try {
input = new Input(baseURI, publicId, systemId, targetURI.toURL().openStream());
}
catch (Exception ex)
{
throw new RuntimeException(
"Could not open resource stream - " + ex.getMessage()
);
}
return input;
}
private static URI getTargetURI(String baseURI, String relativePath)
{
URI targetURI = null;
try {
targetURI = (new URI(baseURI)).resolve(relativePath);
}
catch (URISyntaxException ex)
{
throw new RuntimeException(
"Could not resolve target URI - " + ex.getMessage()
);
}
return targetURI;
}
}
入力.java:
public class Input implements LSInput
{
private BufferedInputStream inputStream;
private String baseURI;
private String publicId;
private String systemId;
public Input(String baseURI, String publicId, String sysId, InputStream input)
{
this.baseURI = baseURI;
this.publicId = publicId;
this.systemId = sysId;
this.inputStream = new BufferedInputStream(input);
}
public String getPublicId()
{
return publicId;
}
public void setPublicId(String publicId)
{
this.publicId = publicId;
}
public String getBaseURI()
{
return baseURI;
}
public InputStream getByteStream()
{
return null;
}
public boolean getCertifiedText()
{
return false;
}
public Reader getCharacterStream()
{
return null;
}
public String getEncoding()
{
return null;
}
public String getStringData()
{
synchronized (inputStream)
{
try {
return IOUtils.toString(inputStream);
}
catch (IOException e) {
e.printStackTrace();
System.out.println("Exception " + e);
return null;
}
}
}
public void setBaseURI(String baseURI) {
}
public void setByteStream(InputStream byteStream) {
}
public void setCertifiedText(boolean certifiedText) {
}
public void setCharacterStream(Reader characterStream) {
}
public void setEncoding(String encoding) {
}
public void setStringData(String stringData) {
}
public String getSystemId() {
return systemId;
}
public void setSystemId(String systemId) {
this.systemId = systemId;
}
public BufferedInputStream getInputStream()
{
return inputStream;
}
public void setInputStream(BufferedInputStream inputStream)
{
this.inputStream = inputStream;
}
}
このソリューションが生成するログを観察すると、実際には include/import ステートメントを深さ優先検索の順序で処理しているように見えます。
ただし、一部の import ステートメントは無視されます (たとえば、まったく処理されません)。
どのステートメントが処理され、どのステートメントが処理されないかのパターンを見つけることができませんでした。たとえば、ファイルの次の 3 行を処理する場合、最初と最初の行のみが処理されます。それらはすべて私と同等に見えますが。
<import namespace="schemas/src/x20130601" schemaLocation="../../x20130601/Personalnumber.xsd"/>
<import namespace="schemas/src/common/2008/01/03" schemaLocation="../../../contract/x20080103/Contractnumber.xsd"/>
<import namespace="schemas/src/20100504" schemaLocation="../../../system/x20100504/Contractidentification.xsd" />
一部の import ステートメントが無視されるという事実は、次のタイプの例外につながります。
Exception in thread "main" org.xml.sax.SAXParseException; systemId: file:/z:/--- project path ---/schemas//x20130504/Identification.xsd; lineNumber: 18; columnNumber: 61; src-resolve: Cannot resolve the name 'dat20080103:Contractnumber' to a(n) 'element declaration' component.
欠落したドキュメントの宣言は、スキップされた XSD ファイルにあります。
エラーを探す場所や追加情報を教えてください。