I had to both use a LSResourceResolver and copy namespace declarations from the wsdl document to the schema elements to make this work. Here is the code:
public static Schema makeSchema(String pathToWsdl)
throws ParserConfigurationException, IOException, SAXException, InstantiationException, IllegalAccessException, ClassNotFoundException {
// read wsdl document
File wsdlFile = new File(pathToWsdl);
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setNamespaceAware(true);
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document wsdlDoc = dBuilder.parse(wsdlFile);
// read namespace declarations from wsdl document, in case they are referred from a schema
NamedNodeMap attributes = wsdlDoc.getDocumentElement().getAttributes();
Map<String, String> namespacesFromWsdlDocument = new HashMap<>();
for (int i = 0; i < attributes.getLength(); i++) {
Node n = attributes.item(i);
if (n.getNamespaceURI() != null && n.getNamespaceURI().equals("http://www.w3.org/2000/xmlns/")) {
namespacesFromWsdlDocument
.put(n.getLocalName(), n.getNodeValue());
}
}
// read the schema nodes from the wsdl
NodeList schemas = wsdlDoc.getElementsByTagNameNS("http://www.w3.org/2001/XMLSchema", "schema");
Map<String, DOMSource> sources = new HashMap<>();
for (int i = 0; i < schemas.getLength(); i++) {
// create a document for each schema and copy the source schema
Document schema = dBuilder.newDocument();
Element schemaElement = (Element)schema.importNode(schemas.item(i), true);
// add all non-existing namespace declarations from the wsdl node
String targetNs = schemaElement.getAttribute("targetNamespace");
for (Map.Entry<String, String> ns : namespacesFromWsdlDocument.entrySet()) {
String name = ns.getKey();
String value = ns.getValue();
if (schemaElement.getAttributeNodeNS("http://www.w3.org/2000/xmlns/", name) == null) {
schemaElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:" + name, value);
}
}
// map schemas by their target namespace
schema.appendChild(schemaElement);
DOMSource domSource = new DOMSource(schema);
sources.put(targetNs, domSource);
}
SchemaFactory factory =
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// Create a ResourceResolver that can find the correct schema from the map
DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
final DOMImplementationLS domImplementationLS = (DOMImplementationLS) registry.getDOMImplementation("LS");
factory.setResourceResolver(new LSResourceResolver() {
@Override public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) {
Source xmlSource = sources.get(namespaceURI);
if (xmlSource != null) {
LSInput input = domImplementationLS.createLSInput();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Result outputTarget = new StreamResult(outputStream);
try {
TransformerFactory.newInstance().newTransformer().transform(xmlSource, outputTarget);
} catch (TransformerException e) {
e.printStackTrace();
}
InputStream is = new ByteArrayInputStream(outputStream.toByteArray());
input.setByteStream(is);
input.setSystemId(systemId);
return input;
} else {
return null;
}
}
});
// create the schema object from the sources
return factory.newSchema(sources.values().toArray(new DOMSource[]{}));
}
Also answered the same here: Java, Validate XML against an embedded schema in WSDL