In JAXB output I'm trying to populate the xsi:schemaLocation attribute so it contains complete information for all the namespaces and schemas that were used. I am trying to find a better way than hardcoding it!
We have a XML schema that's spread across about fifty files. Each file defines a type and has its own namespace. (Not a smart decision I think, but too late to argue that one.) XJC happily compiles the whole mess and generates classes. When the output is marshalled all the 50+ namespaces are properly put on the root element.
The problem is that I've been asked to populate the xsi:schemaLocation attribute with all the namespaces and associated schema sources. So instead of just one namespace-URL pair ("my.co.schema.v1 http://my.company.com/schemas/my.co.schema.v1.xsd"), I will need to put 50+ pairs in there. Sure I can hardcode it but that's wrong.
I'd like to query JAXB about all the sources it used, but that doesn't seem possible. I'm starting to think I have to use a Xerces grammar pool and read all the schemas in again. Is that the best option?
I added a SCCE below. Works fine, but as I mentioned, the xsi:schemaLocation attribute is hardcoded and incomplete.
Please help, thanks in advance.
package my.xml.generator;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import my.schema.ComponentType;
import my.schema.ObjectFactory;
import my.schema.ProductType;
/**
* Builds a JAXB object and serializes to a file.
*
* Many thanks to
* http://blog.bdoughan.com/2010/12/jaxb-and-marshalunmarshal-schema.html
*/
public class SimpleXmlBuilder {
public static void main(String[] argv) throws Exception {
if (argv.length != 1)
throw new IllegalArgumentException(
"Usage: SimpleXmlBuilder output-file-path");
// Create a trivial component
ObjectFactory objectFactory = new ObjectFactory();
ProductType product = objectFactory.createProductType();
product.setDeprecated(true);
// Wrap in a root element
JAXBElement<ProductType> rootElement = objectFactory
.createProduct(product);
// Get JAXB going
Class<?>[] rootElementClass = { ComponentType.class };
JAXBContext context = JAXBContext.newInstance(rootElementClass);
Marshaller marshaller = context.createMarshaller();
// Leave some whitespace for humans
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
// The critical part of the problem!
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,
"My.Name.Space.v1" + " "
+ "http://my.server.com/schemas/My.Name.Space.v1.xsd");
// Write to the named file
marshaller.marshal(rootElement, new File(argv[0]));
System.out.println("Wrote generated XML to " + argv[0]);
}
}
You could leverage the javax.xml.validation APIs to resolve all the schema locations with an LSResourceResolver.
import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*;
import org.w3c.dom.ls.*;
public class Demo {
public static void main(String[] args) throws Exception {
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
MyLSResourceResolver resourceResolver = new MyLSResourceResolver();
sf.setResourceResolver(resourceResolver);
StreamSource rootSchemaSource = new StreamSource("src/forum17992435/schema1.xsd");
sf.setResourceResolver(resourceResolver);
Schema schema = sf.newSchema(rootSchemaSource);
}
private static class MyLSResourceResolver implements LSResourceResolver {
@Override
public LSInput resolveResource(String type, String namespaceURI,
String publicId, String systemId, String baseURI) {
System.out.print(namespaceURI);
System.out.print(" - ");
System.out.println(systemId);
return null;
}
}
}
schema1.xsd
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/schema1" xmlns:tns="http://www.example.org/schema1" elementFormDefault="qualified">
<import namespace="http://www.example.org/schema2" schemaLocation="schema2.xsd"/>
</schema>
schema2.xsd
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/schema2" xmlns:tns="http://www.example.org/schema2" elementFormDefault="qualified">
<import namespace="http://www.example.org/schema3" schemaLocation="schema3.xsd"/>
</schema>
schema3.xsd
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/schema3" xmlns:tns="http://www.example.org/schema3" elementFormDefault="qualified">
</schema>
The demo will output the locations of the 2 imported schemas. You will need to provide the location of the first XML schema.
http://www.example.org/schema2 - schema2.xsd
http://www.example.org/schema3 - schema3.xsd
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With