JAXB SchemaFactory source order must follow import order between schemas?
Using latest JAXB (Sun) and have a hierarchy of schemas that use import directives between schemas to share type definitions. Schema validation is activated on the setSchema call to Marshaller/Unmarshaller in JAXB which should defer validation to Xerces (using Java 1.5). I don't want to know the order of import directives between schemas when creating the Schema object with SchemaFactory. Unfortunately, I haven't found a Xerces feature/property that allows for this. For example, if a.xsd is pulled into b.xsd with an import then the following code doesn't work:
FileInputStrea开发者_JS百科m a = new FileInputStream("a.xsd");
FileInputStream b = new FileInputStream("b.xsd");
Schema schema = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(
new Source[] {
new StreamSource(b),
new StreamSource(a)
}
);
The order of the Source array has to be a.xsd then b.xsd. Any way around this?
What if you create a schema on the root Source, and then set a ResourceResolver (LSResourceResolver) to resolve the other imported schemas during the schema creation.
Late post of the code.
Generate a validation Schema with:
SchemaFactory factory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
factory.setResourceResolver(new SimpleResolver(streams));
....
Schema schemaGrammers = factory.newSchema(streams.toArray(new SchemaSource[0]));
The Schema (schemaGrammers object) gets injected into the Marshaller:
Marshaller m = ...createMarshaller();
m.setSchema(<schemaGrammers>);
And the SimpleResolver implements the LSResourceResolver class:
private class SimpleResolver implements LSResourceResolver {
private Set<Source> streams;
public SimpleResolver(Set<Source> streams) {
this.streams = streams;
}
@Override
public LSInput resolveResource(String type, String namespaceURI,
String publicId, String systemId, String baseURI) {
DOMImplementationRegistry registry;
try {
registry = DOMImplementationRegistry.newInstance();
DOMImplementationLS domImplementationLS = (DOMImplementationLS) registry
.getDOMImplementation("LS 3.0");
LSInput ret = domImplementationLS.createLSInput();
for (Source source : streams) {
SchemaSource schema = (SchemaSource) source;
if (schema.getResourceName().equals(
schema.getResourceName(systemId))
& schema.getTargetNamespace().equals(namespaceURI)) {
logger.debug(
"Resolved systemid [{}] with namespace [{}]",
schema.getResourceName(systemId), namespaceURI);
URL url = new URL(schema.getSystemId());
URLConnection uc = url.openConnection();
ret.setByteStream(uc.getInputStream());
ret.setSystemId(systemId);
return ret;
}
}
} catch (ClassCastException e) {
logger.error(e.getMessage());
} catch (ClassNotFoundException e) {
logger.error(e.getMessage());
} catch (InstantiationException e) {
logger.error(e.getMessage());
} catch (IllegalAccessException e) {
logger.error(e.getMessage());
} catch (FileNotFoundException e) {
logger.error(e.getMessage());
} catch (IOException e) {
logger.error(e.getMessage());
}
logger.error("No stream found for system id [{}]", systemId);
return null;
}
}
A new input stream has to be created otherwise a conflict occurs. Not sure why (didn't bother to debug the code) but the streams I pass to the constructor [ie. the Set object] have already been read.
精彩评论