Does JAXB support default schema values?
I have a schema that defines default values for elements and attributes. I am trying to parse a document using JAXB based on that schema but JAXB is not setting the default values. Any ideas on how to make JAXB honor the default values from the schema?
example.xsd:
<?xml version="1.0" encoding="UTF-8"?><xs:schemaxmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org/example"
xmlns:tns="http://www.example.org/example">
<xs:element name="root" type="tns:rootType"/>
<xs:complexType name="rootType">
<xs:sequence>
<xs:element name="child" type="tns:childType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="childType">
<xs:sequence>
<xs:element name="childVal" type="xs:string" default="defaultElVal"/>
</xs:sequence>
<xs:attribute name="attr" type="xs:string" default="defaultAttrVal"/>
</xs:complexType>
example1.xml
<?xml version="1.0" encoding="UTF-8"?>
<tns:root xmlns:tns="http://www.example.org/example" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/example example.xsd ">
<child>
<childVal/>
</child>
</tns:root>
TestParser.java
package test;
import java.io.File;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
public class TestParser {
public static void main(String[] pArgs) {
try {
JAXBContext context = JAXBContext.newInstance(RootElement.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
SchemaFactory schemaFac = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema sysConfigSchema = schemaFac.newSchema(
new File("example.xsd"));
unmarshaller.setSchema(sysConfigSchema);
RootElement root = (RootElement)unmarshaller.unmarshal(
new File("example1.xml"));
System.out.println("Child Val: " + root.getChild().getChildVal());
System.out.println("Child Attr: " + root.getChild().getAttr());
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
RootElement.java
package test;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="root", namespace="http://w开发者_JAVA百科ww.example.org/example")
public class RootElement {
private ChildEl child;
public RootElement() {}
public ChildEl getChild() {
return child;
}
public void setChild(ChildEl pChild) {
this.child = pChild;
}
}
ChildEl.java
package test;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="child")
public class ChildEl {
private String attr;
private String childVal;
public ChildEl() {};
@XmlAttribute
public String getAttr() {
return attr;
}
public void setAttr(String pAttr) {
this.attr = pAttr;
}
public String getChildVal() {
return childVal;
}
public void setChildVal(String pVal) {
this.childVal = pVal;
}
}
Element Default Value
To get the default value on the element property you need to annotate it as follows:
@XmlElement(defaultValue="defaultElVal")
public String getChildVal() {
return childVal;
}
Attribute Default Value
If you use EclipseLink JAXB (MOXy) you will get the default attribute value using the code you supplied. There may be a bug in the Metro implementation of JAXB that is preventing this from working. Note I lead the MOXy implementation.
Alternate Approach
The following code should work with any JAXB implementation without requiring any code changes to your model. You could do the following and leverage SAXSource:
import java.io.File;
import java.io.FileInputStream;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
public class TestParser {
public static void main(String[] pArgs) {
try {
JAXBContext context = JAXBContext.newInstance(RootElement.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
SchemaFactory schemaFac = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema sysConfigSchema = schemaFac.newSchema(
new File("example.xsd"));
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
spf.setSchema(sysConfigSchema);
XMLReader xmlReader = spf.newSAXParser().getXMLReader();
SAXSource source = new SAXSource(xmlReader, new InputSource(new FileInputStream("example1.xml")));
RootElement root = (RootElement)unmarshaller.unmarshal(
source);
System.out.println("Child Val: " + root.getChild().getChildVal());
System.out.println("Child Attr: " + root.getChild().getAttr());
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
I find what you're trying to do as sensible, especially to have harmony with Attribute vs. Simple Elements. It seems odd me, but it seems the jaxb2 implementors choose to add an extra set of extensions which you have to add to get the desired behavior. See:
- http://java.net/projects/jaxb2-commons/pages/Default-Value
(I would have rather seen more natural default behaviors and consistency between Attributes and Elements of at least simple types -- without having to register a plugin. Then provide a plug-in for special cases. My only guys on why this wasn't done was or backwards compatibility--a guess.)
The jaxb2-commons default value plugin refers to an extra commands (and jars) you add to xjc which in turn adds default behaviors to the field. In my case:
public String getScalarOptionalMaxAndDefaultString() {
if (scalarOptionalMaxAndDefaultString == null) {
return "def val 1";
} else {
return scalarOptionalMaxAndDefaultString;
}
}
(Where, of course, the conditional null check presents the default value or not.)
Using Blaise Doughan seems like an practical work around. Depending the nature of your XML doc, this may be perfect.
Yet, it seems this Default Value plugin might move the solution to the build process and not see a change to your code (assuming you're using a Dom as opposed to Sax parser Blaise suggested).
It looks the default-value plugin solve the problem and possibly provide additional extensibility (haven't needed such advanced customization) in the unlikely event you require even more programatic default value control running xjc.
Here is a maven config snippet in case it helps:
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.8.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<args>
<arg>-Xdefault-value</arg>
</args>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-default-value</artifactId>
<version>1.1</version>
</plugin>
</plugins>
</configuration>
</execution>
</executions>
<configuration><schemaDirectory>src/test/resources</schemaDirectory></configuration>
</plugin>
Another way for setting the default value can be a beforeMarshal(Marshaller marshaller) function:
private void beforeMarshal(Marshaller marshaller) {
childVal = (null == getChildVal) ? CHILD_VAL_DEFAULT_VALUE : childVal; }
精彩评论