Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get all namespaces and URLs to populate xsi:schemaLocation attribute when marshalling a JAXB model

Tags:

xml

jaxb

schema

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]);
  }

}
like image 830
chrisinmtown Avatar asked Nov 30 '25 21:11

chrisinmtown


1 Answers

Demo Code

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;
        }

    }

}

XML Schemas

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>

Output

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
like image 198
bdoughan Avatar answered Dec 02 '25 13:12

bdoughan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!