Reputation: 3751
I have a .wsdl
file with an embedded schema. I want to validate an XML file/string using that .wsdl
file (the same way you would validate against an .xsd
). The schema is between the <types>
tag. I have this so far:
public boolean validate(String xmlString) {
try {
// Convert to input stream
InputStream xml = new ByteArrayInputStream(xmlString.getBytes());
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(new File("wsdl_filepath"));
// Validate against wsdl
Validator validator = schema.newValidator();
validator.validate(new StreamSource (xml));
// XML Message is valid
return true;
} catch (SAXException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
This doesn't work, however. This validator works if validating against an .xsd
. I'm not sure how to modify it to validate against the embedded schema.
Any help would be appreciated, thank you!
Upvotes: 5
Views: 5216
Reputation: 867
Expanding on @morten's answer, I've found that one can generate the schema files using the JAXB-annotated classes instead of grabbing the schemas directly from the WSDL file. Adding the resource resolver to the schema factory is still required when using this method.
private static Schema createSchemaFromType(final Class<?> type) throws Exception {
final AtomicInteger sequence = new AtomicInteger(1);
final ByteArrayOutputStream schemasOutputStream = new ByteArrayOutputStream();
final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setNamespaceAware(true);
// Generate the schemas from the JAXB annotated classes and put the output in the schemasOutputStream.
JAXBContext.newInstance(type).generateSchema(new SchemaOutputResolver() {
@Override
public Result createOutput(final String namespaceUri, final String suggestedFileName) {
final Result result = new StreamResult(schemasOutputStream);
result.setSystemId("sys" + sequence.getAndIncrement());
return result;
}
});
// The result of the previous operation puts all schemas in a byte array. Transform the byte array output into
// a string, which will contain a number of different schemas, then split the string to put each individual
// schema in its own string.
final byte[] schemasOutputBytes = schemasOutputStream.toByteArray();
final String schemasOutputString = new String(schemasOutputBytes, StandardCharsets.UTF_8);
final String schemasOutputStringDelimited = schemasOutputString.replace("<?xml", "<<<START OF SCHEMA>>><?xml");
final String[] schemaStrings = schemasOutputStringDelimited.split("<<<START OF SCHEMA>>>");
final Map<String, DOMSource> schemasSourcesByNamespace = new HashMap<>();
// Map the schema documents by their target namespace.
for (final String schemaString : schemaStrings) {
if (schemaString.trim().length() > 0) {
final String schema = schemaString.replace("schemaLocation=\".*?\"", "");
final ByteArrayInputStream schemaInputStream = new ByteArrayInputStream(schema.getBytes(StandardCharsets.UTF_8));
final DocumentBuilder documentBuilder = dbFactory.newDocumentBuilder();
final Document schemaDocument = documentBuilder.parse(schemaInputStream);
final Element schemaElement = schemaDocument.getDocumentElement();
final String schemaTargetNamespace = schemaElement.getAttribute("targetNamespace");
schemasSourcesByNamespace.put(schemaTargetNamespace, new DOMSource(schemaDocument));
}
}
// Create the schema factory in a way that it can resolve the schemas for all the namespaces previously created.
final DOMImplementationRegistry domImplementationRegistry = DOMImplementationRegistry.newInstance();
final DOMImplementationLS domImplementation = (DOMImplementationLS) domImplementationRegistry.getDOMImplementation("LS");
final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setResourceResolver(new LSResourceResolver() {
@Override
public LSInput resolveResource(
final String type,
final String namespaceUri,
final String publicId,
final String systemId,
final String baseUri
) {
final Source xmlSource = schemasSourcesByNamespace.get(namespaceUri);
if (xmlSource != null) {
final LSInput input = domImplementation.createLSInput();
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
final Result outputTarget = new StreamResult(outputStream);
try {
transformerFactory.newTransformer().transform(xmlSource, outputTarget);
} catch (final TransformerException e) {
throw new RuntimeException(
"Failed to transform schema document for namespace '" + namespaceUri + "' into an " +
"output stream due to an unexpected error.",
e
);
}
final InputStream is = new ByteArrayInputStream(outputStream.toByteArray());
input.setByteStream (is);
input.setSystemId (systemId);
return input;
} else {
return null;
}
}
});
// Create the schema using the schema sources.
return schemaFactory.newSchema(schemasSourcesByNamespace.values().toArray(new DOMSource[]{}));
}
Upvotes: 0
Reputation: 401
Here is what worked for me to extract the schemas from wsld:
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[]{}));
}
Upvotes: 5