Linus
Linus

Reputation: 1518

JAXB how to dynamically generate a schema to use it for validation immediately?

I've spent a long time trying to figure out how to validate a XML file, and I've got it working with a pre-generated XSD schema. However, I wish to dynamically generate a schema without creating a file, based on the annotation class I have specified, I've tried to not specify any parameters to SchemaFactory, but then it seems to just create an empty schema (see comments below).

Here are the two classes I use for my JAXB reading and writing of a XML file.

This is the code for the class XMLTranslationWrapper:

@XmlAccessorType(XmlAccessType.FIELD)

@XmlRootElement(namespace = "my.package.namespace")
public class XMLTranslationWrapper {

      @XmlElementWrapper(name   = "TRANSLATIONS")
      @XmlElement(name          = "TRANSL")
      public ArrayList<XMLTranslationNode> translations;

      public XMLTranslationWrapper(){
          translations = new ArrayList<XMLTranslationNode>();
      }

      public void setTranslations(ArrayList<XMLTranslationNode> translations){
          this.translations = translations;
      }

      public XMLTranslationNode getTranslation(String code){
          for(XMLTranslationNode transl : translations){
              if(transl.getCode().equals(code))
                  return transl;
          }
        return null;
      }

      public void addTranslation(XMLTranslationNode translation){
          this.translations.add(translation);
      }
}

This is the code for the class XMLTranslationNode:

@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlRootElement(name = "TRANSL")

@XmlType(propOrder = { "severity", "translation" })
public class XMLTranslationNode {

    private String severity;

    private String code;
    private String translation;

    @XmlElement(name="SEVERITY")
    public String getSeverity(){
        return this.severity;
    }

    public void setSeverity(String severity){
        this.severity = severity;
    }

    @XmlAttribute(name="CODE")
    public String getCode(){
        return this.code;
    }

    public void setCode(String code){
        this.code = code;
    }

    @XmlElement(name="TRANSLATION")
    public String getTranslation(){
        return this.translation;
    }

    public void setTranslation(String translation){
        this.translation = translation;
    }

}

This is the code I used to generate the pre-generated XSD schema:

public class generateSchema {

    public static void main(String[] args) {
        JAXBContext jaxbContext;
        try {
            jaxbContext = JAXBContext.newInstance(XMLTranslationWrapper.class);
            SchemaOutputResolver sor = new MySchemaOutputResolver();
            jaxbContext.generateSchema(sor);
        } catch (JAXBException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static class MySchemaOutputResolver extends SchemaOutputResolver {

        public Result createOutput(String namespaceURI, String suggestedFileName) throws IOException {
            File file = new File(suggestedFileName);
            StreamResult result = new StreamResult(file);
            System.out.println(file.toURI().toURL().toString());
            result.setSystemId(file.toURI().toURL().toString());
            return result;
        }

    }

}

Finally here is a sample XML which can be used to test the code:

<?xml version="1.0" encoding="US-ASCII" standalone="yes"?>
<ns2:xmlTranslationWrapper xmlns:ns2="my.package.namespace">
    <TRANSLATIONS>
        <TRANSL CODE="123">
            <SEVERITY>Information</SEVERITY>
            <TRANSLATION>ABC</TRANSLATION>
        </TRANSL>
    </TRANSLATIONS>
</ns2:xmlTranslationWrapper>

How can I dynamically generate my XSD schema without creating a file equivalent to using a pre-generated XSD schema?

Upvotes: 0

Views: 2451

Answers (1)

lexicore
lexicore

Reputation: 43709

As promised. The idea is simple:

  • First generate your schema into a DOM result
  • The parse it from the generated DOM
  • And, finally, use for validation

I don't think you can do it much better than that. JAXB's schemagen internal structures do not seem to be compatible with javax.xml.validation.Schema. So creating DOM and then parsing it back is the easiest way.

Code example:

public class DynamicSchemaTest {

    @XmlRootElement
    public static class A {
        @XmlAttribute(required = true)
        public String name;

        public A() {
        }

        public A(String name) {
            this.name = name;
        }
    }

    @Test(expected = MarshalException.class)
    public void generatesAndUsesSchema() throws JAXBException, IOException,
            SAXException {
        final JAXBContext context = JAXBContext.newInstance(A.class);
        final DOMResult result = new DOMResult();
        result.setSystemId("schema.xsd");
        context.generateSchema(new SchemaOutputResolver() {
            @Override
            public Result createOutput(String namespaceUri,
                    String suggestedFileName) {
                return result;
            }
        });

        @SuppressWarnings("deprecation")
        final SchemaFactory schemaFactory = SchemaFactory
                .newInstance(WellKnownNamespace.XML_SCHEMA);
        final Schema schema = schemaFactory.newSchema(new DOMSource(result
                .getNode()));

        final Marshaller marshaller = context.createMarshaller();
        marshaller.setSchema(schema);
        // Works
        marshaller.marshal(new A("works"), System.out);
        // Fails
        marshaller.marshal(new A(null), System.out);
    }
}

Upvotes: 5

Related Questions