Jaxb: xs:attribute null values - jaxb

Reg: Jaxb
I'm basically trying to set up a role in JAXB which says that whenever an null field is encountered, instead of ignoring it in the output, set it to an empty value.
For xmlElement I got answer like we need to use nillable="true" but for how we need to set the null value. by googling I found that we need to use use="optional" but its not working in my case.
My xsd's part is below:
<xs:attribute name="RomVersion" type="xs:string" use="required" />
<xs:attribute name="MACAddress" type="xs:string" use="required" />
<xs:attribute name="LargestFreeBlock" type="xs:unsignedInt" use="required" />
<xs:attribute name="TimeSinceLastReset" type="xs:unsignedInt" use="optional" />
<xs:attribute name="ResetReason" type="xs:string" use="optional" />
<xs:attribute name="TimeStamp" type="xs:unsignedInt" use="optional" />
<xs:attribute name="ECOList" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
Please give me the solution ASAP if anyone knows.

Starting from XML Schema
In a previous answer I described how to solve your use case when starting from Java objects. Based on your comments to that answer, this answer describes how the same thing can be done when the model is generated from an XML schema.
XML Schema (attributeAdapter.xsd)
For this example we will use the following XML schema:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema
elementFormDefault="qualified"
targetNamespace="http://www.example.com/adapter"
xmlns:nytd="http://www.example.com/adapter"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:attribute name="foo" type="xs:string"/>
<xs:attribute name="bar" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:schema>
StringConverter
We will need to define a class to do our special String handling. For this use case we want a null field/property value to be treated as empty String ("") in the XML document:
package com.example.adapter;
public class StringConverter {
public static String parseString(String value) {
if("".equals(value)) {
return null;
}
return value;
}
public static String printString(String value) {
if(null == value) {
return "";
}
return value;
}
}
Binding File (attributeAdapterBinding.xml)
We will need to use a JAXB binding file to customize the class generation. The binding file below will allow us to leverage the StringConverter class that we defined above:
<jaxb:bindings
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:bindings schemaLocation="attributeAdapter.xsd">
<jaxb:bindings node="//xs:element[#name='root']/xs:complexType">
<jaxb:bindings node="xs:attribute[#name='foo']">
<jaxb:property>
<jaxb:baseType>
<jaxb:javaType name="java.lang.String"
parseMethod="com.example.adapter.StringConverter.parseString"
printMethod="com.example.adapter.StringConverter.printString"/>
</jaxb:baseType>
</jaxb:property>
</jaxb:bindings>
<jaxb:bindings node="xs:attribute[#name='bar']">
<jaxb:property>
<jaxb:baseType>
<jaxb:javaType name="java.lang.String"
parseMethod="com.example.adapter.StringConverter.parseString"
printMethod="com.example.adapter.StringConverter.printString"/>
</jaxb:baseType>
</jaxb:property>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
XJC call
We will make our XJC call as follows:
xjc -d out -b attributeAdapterBinding.xml attributeAdapter.xsd
Domain Model (Root)
The fields/properties that we customized in the binding file will be annotated with #XmlJavaTypeAdapter;
package com.example.adapter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "")
#XmlRootElement(name = "root")
public class Root {
#XmlAttribute
#XmlJavaTypeAdapter(Adapter1 .class)
protected String foo;
#XmlAttribute
#XmlJavaTypeAdapter(Adapter2 .class)
protected String bar;
public String getFoo() {
return foo;
}
public void setFoo(String value) {
this.foo = value;
}
public String getBar() {
return bar;
}
public void setBar(String value) {
this.bar = value;
}
}
XmlAdapter (Adapter1)
The generated XmlAdapter class will look something like the following. Note how it leverages our StringConverter class:
package com.example.adapter;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class Adapter1 extends XmlAdapter<String, String> {
public String unmarshal(String value) {
return (com.example.adapter.StringConverter.parseString(value));
}
public String marshal(String value) {
return (com.example.adapter.StringConverter.printString(value));
}
}
Demo
Now if we run the following demo code:
package com.example.adapter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
root.setFoo(null);
root.setBar(null);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
We will get the desired output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns="http://www.example.com/adapter" foo="" bar=""/>
UPDATE (Alternate Binding File)
Alternatively, if you wanted the adapter applied to all properties of type xsd:string then you could use an binding file that looked something like;
<jaxb:bindings
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:globalBindings>
<jaxb:javaType
name="String"
xmlType="xs:string"
parseMethod="com.example.adapter.StringConverter.parseString"
printMethod="com.example.adapter.StringConverter.printString"/>
</jaxb:globalBindings>
</jaxb:bindings>

Starting from Java Objects
For fields/properties mapped as #XmlAttribute, a JAXB implementation (Metro, MOXy, JaxMe, etc) will marshal an empty String ("") value as property="". You can use an XmlAdapter to expose your null values as empty Strings to get the desired behaviour:
NullStringAdapter
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class NullStringAdapter extends XmlAdapter<String, String> {
#Override
public String unmarshal(String v) throws Exception {
if("".equals(v)) {
return null;
}
return v;
}
#Override
public String marshal(String v) throws Exception {
if(null == v) {
return "";
}
return v;
}
}
Root
The following is how you specify the adapter in your domain model. The same adapter can be used on many properties:
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class Root {
private String foo;
private String bar;
#XmlAttribute
#XmlJavaTypeAdapter(NullStringAdapter.class)
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
#XmlAttribute
#XmlJavaTypeAdapter(NullStringAdapter.class)
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
Demo
You can demonstrate the concept, by running the following demo code:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
root.setFoo(null);
root.setBar(null);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
The following is the demo code output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root bar="" foo=""/>
For More Information on JAXB's XmlAdapter See:
http://bdoughan.blogspot.com/2010/07/xmladapter-jaxbs-secret-weapon.html
http://bdoughan.blogspot.com/2010/12/jaxb-and-immutable-objects.html
http://bdoughan.blogspot.com/2010/12/represent-string-values-as-element.html

You may use default values plugin for that.
Please look that question: JAXB xjc: How to generate code for Strings that returns empty if the value is null?

Related

SAX parser doesn't follow references

I am trying to parse an HL7 message definition from xsd. I have my schema definition split up between two files. First file contains actual message definition and the second contains segment definitions within the message.
I am trying to tweak an example code to parse XML from here https://gist.github.com/helderdarocha/8791651. I don't understand why SAX parser doesn't follow references.
Here are two examples of my xsd definitions.
First file has the following definition
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
targetNamespace="http://www.xsd_porcessor.org/parser"
xmlns="http://www.xsd_porcessor.org/parser"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:include schemaLocation="segments.xsd"/>
<xs:complexType name="ADT.01.MESSAGE">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="1" ref="MSH"/>
<xs:element maxOccurs="1" minOccurs="1" ref="EVN"/>
<xs:element maxOccurs="1" minOccurs="1" ref="PID"/>
<xs:element maxOccurs="1" minOccurs="1" ref="PV1"/>
<xs:element maxOccurs="1" minOccurs="1" ref="IN1"/>
<xs:element maxOccurs="1" minOccurs="1" ref="IN2"/>
</xs:sequence>
</xs:complexType>
<xs:element name="ADT.A01" type="ADT.01.MESSAGE"/>
</xs:schema>
The second file has the following header
<?xml version="1.1" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
targetNamespace="http://www.xsd_porcessor.org/parser"
xmlns="http://www.xsd_porcessor.org/parser"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
...and a multitude segment definitions represented as complexTypes. Bellow is example of one
<xs:complexType name="MSH.SEGMENT">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="1" ref="MSH.1.FieldSeparator"/>
<xs:element maxOccurs="1" minOccurs="1" ref="MSH.2.ServiceString"/>
<xs:element maxOccurs="1" minOccurs="1" ref="MSH.3.SendingApplication"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.4.SendingFacility"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.5.ReceivingApplication"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.6.ReceivingFacility"/>
<xs:element maxOccurs="1" minOccurs="1" ref="MSH.7.DateTimeOfMessage"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.8.Security"/>
<xs:element maxOccurs="1" minOccurs="1" ref="MSH.9.MessageType"/>
<xs:element maxOccurs="1" minOccurs="1" ref="MSH.10.MessageControlID"/>
<xs:element maxOccurs="1" minOccurs="1" ref="MSH.11.ProcessingID"/>
<xs:element maxOccurs="1" minOccurs="1" ref="MSH.12.VersionID"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.13.SequenceNumber"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.14.ContinuationPointer"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.15.AcceptAcknowledgmentType"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.16.ApplicationAcknowledgmentType"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.17.CountryCode"/>
<xs:element maxOccurs="unbounded" minOccurs="0" ref="MSH.18.CharacterSet"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.19.PrincipalLanguageOfMessage"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.20.AlternateCharacterSetHandlingScheme"/>
<xs:element maxOccurs="unbounded" minOccurs="0" ref="MSH.21.MessageProfileIdentifier"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.22.SendingResponsibleOrganization"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.23.ReceivingResponsibleOrganization"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.24.SendingNetworkAddress"/>
<xs:element maxOccurs="1" minOccurs="0" ref="MSH.25.ReceivingNetworkAddress"/>
</xs:sequence>
</xs:complexType>
<xs:element name="MSH" type="MSH.SEGMENT"/>
Here is a tweaked parser itself
package ca.parser.xml;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
public class SAXReaderExample {
public static final String PATH = "resources";
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader reader = sp.getXMLReader();
reader.setContentHandler(new SchemaSaxHandler());
reader.parse(new InputSource(new FileInputStream(new File(PATH, "messages.xsd"))));
}
}
class SchemaSaxHandler extends DefaultHandler {
// temporary - always null when tag closes
private String currentSimpleTypeName;
private String currentSimpleTypeBaseType;
private SchemaElement currentElement;
private SchemaComplexType currentComplexType;
private List<SchemaElement> currentSequence;
// cumulative - will use the data when XML finishes
private Map<String, String> simpleTypes = new HashMap<>();
private Map<String, SchemaComplexType> complexTypes = new HashMap<>();
private SchemaElement rootElement;
#Override
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
if (qName.equals("xs:simpleType")) {
currentSimpleTypeName = atts.getValue("name");
}
if (qName.equals("xs:restriction")) {
currentSimpleTypeBaseType = atts.getValue("base");
}
if (qName.equals("xs:complexType")) {
currentComplexType = new SchemaComplexType();
currentComplexType.setName(atts.getValue("name"));
}
if (qName.equals("xs:sequence")) {
currentSequence = new ArrayList<>();
}
if (qName.equals("xs:element")) {
currentElement = new SchemaElement();
if (atts.getValue("name")==null) {
currentElement.setName(atts.getValue("ref"));
}else {
currentElement.setName(atts.getValue("name"));
}
currentElement.setType(atts.getValue("type"));
currentElement.setReference(atts.getValue("ref"));
if (currentSequence != null) {
currentSequence.add(currentElement);
} else {
rootElement = currentElement;
}
}
if (qName.equals("xs:attribute")) {
currentComplexType.addAttribute(atts.getValue("name"), atts.getValue("type"));
}
}
#Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.equals("xs:simpleType")) {
simpleTypes.put(currentSimpleTypeName, currentSimpleTypeBaseType);
currentSimpleTypeName = null;
currentSimpleTypeBaseType = null;
}
if (qName.equals("xs:complexType")) {
complexTypes.put(currentComplexType.getName(), currentComplexType);
currentComplexType = null;
}
if (qName.equals("xs:sequence")) {
if (currentComplexType != null) {
currentComplexType.setChildren(currentSequence);
}
currentSequence = null;
}
}
#Override
public void endDocument() throws SAXException {
makeTree(rootElement);
printTree(rootElement, "");
}
public void makeTree(SchemaElement element) {
SchemaComplexType type = complexTypes.get(element.getType());
if (type != null) {
List<SchemaElement> children = type.getChildren();
element.setChildren(children);
for (SchemaElement child : children) {
makeTree(child);
}
element.setAttributes(type.getAttributes());
} else {
element.setType(simpleTypes.get(element.getType()));
}
}
private void printTree(SchemaElement element, String indent) {
System.out.println(indent + element.getName() + " : " + element.getType());
Map<String, String> attributes = element.getAttributes();
if (attributes != null) {
for (Map.Entry<String, String> entry : attributes.entrySet()) {
System.out.println(" #" + entry.getKey() + " : " + simpleTypes.get(entry.getValue()));
}
}
List<SchemaElement> children = element.getChildren();
if (children != null) {
for (SchemaElement child : children) {
printTree(child, indent + " ");
}
}
}
class SchemaElement {
private String name;
private String type;
private String reference;
public String getReference() {
return reference;
}
public void setReference(String reference) {
this.reference = reference;
}
private List<SchemaElement> children;
private Map<String, String> attributes;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<SchemaElement> getChildren() {
return children;
}
public void setChildren(List<SchemaElement> children) {
this.children = children;
}
public Map<String, String> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}
}
class SchemaComplexType {
private String name;
private String reference;
private List<SchemaElement> children;
private Map<String, String> attributes = new HashMap<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<SchemaElement> getChildren() {
return children;
}
public void setChildren(List<SchemaElement> children) {
this.children = children;
}
public Map<String, String> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}
public String getReference() {
return reference;
}
public void setReference(String reference) {
this.reference=reference;
}
public void addAttribute(String name,String type) {
attributes.put(name, type);
}
}
Any ideas what is going? You help is appreciated.
Thank you.
It sounds like there are two separate concepts at work here.
If a validating SAX parser is being used to parse a piece of XML, and validate it against its schema:
<xmlRootElement
xmlns="http://www.xsd_porcessor.org/parser"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.xsd_porcessor.org/parser messages.xsd">
... etc, then clearly when that schema was resolved behind the scenes, the parser would need to follow any references and imports in there.
However if the .xsd itself is the XML being parsed, then as you've already found, it's elements will be directly passed into the ContentHandler. The SchemaSaxHandler above will need to do some more work to learn each xs:element - like you're already doing for the simpleTypes and complexTypes Maps - so they can later be resolved from a ref.
If what you need is the model of the resolved elements and types in the XML schema though, it would be worth exploring that behind the scenes schema model - in an XML Parser such as Xerces. As a starting point, this is using XNI - the Xerces Native Interface:
File baseDir = new File("/myschemas");
XMLEntityResolver entityResolver = new XMLEntityResolver() {
#Override
public XMLInputSource resolveEntity(
XMLResourceIdentifier resourceIdentifier)
throws XNIException, IOException {
// E.g. resourceIdentifier.getLiteralSystemId() will be segments.xsd
String uri = new File(baseDir,
resourceIdentifier.getLiteralSystemId()).toURI()
.toString();
return new XMLInputSource(null, uri, null);
}
};
XMLSchemaLoader loader = new XMLSchemaLoader();
loader.setEntityResolver(entityResolver);
XSModel model = loader
.loadURI(new File(baseDir, "messages.xsd").toURI()
.toString());
System.out.println(model.getComponents(XSConstants.ELEMENT_DECLARATION));
This outputs such as:
{http://www.xsd_porcessor.org/parser}ADT.A01="http://www.xsd_porcessor.org/parser":ADT.A01

Bug in org.eclipse.persistence.jaxb.compiler.SchemaGenerator with inheritance, #XmlAttribute and #XmlAnyAttribute?

The issues:
Is there a but in MOXy's SchemaGenerator with inheritance, #XmlAttribute and #XmlAnyAttribute?
If so, how can I assist in fixing it? And when would a fix be planned?
I have two classes:
One abstract class (AbstractKeyedItem) that may have an #XmlAttribute "key"
A concrete class (MyAnyAttr) that extends AbstractKeyedItem, has a #XmlAnyAttribute, and has an #XmlElement list of MyAnyAttr
The problem is that the default Oracle (Sun) XmlSchemaGenerator generates a valid XML Schema, but that the Eclipselink/MOXy SchemaGenerator doesn't seem to do so.
Eclipse reports on the invalid XSD:
s4s-elt-invalid-content.1: The content of 'myAnyAttr' is invalid. Element 'anyAttribute' is invalid, misplaced, or occurs too often.
xmllint reports:
Element '{http://www.w3.org/2001/XMLSchema}complexType': The content is not valid. Expected is (annotation?, (simpleContent | complexContent | ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?)))).
I think to have isolated the problem. I provide all (most) required resources.
XSD as generated by MOXy (xsd:anyAttribute is the offending element):
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="myAnyAttr">
<xsd:complexContent>
<xsd:extension base="abstractKeyedItem">
<xsd:sequence>
<xsd:element name="kid" type="myAnyAttr" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
<xsd:anyAttribute processContents="skip" namespace="##other"/>
</xsd:complexType>
<xsd:complexType name="abstractKeyedItem" abstract="true">
<xsd:sequence/>
<xsd:attribute name="key" type="xsd:string"/>
</xsd:complexType>
<xsd:element name="my-any-attr-root" type="myAnyAttr"/>
</xsd:schema>
The XSD generated by Oracle:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="my-any-attr-root" type="myAnyAttr"/>
<xs:complexType name="myAnyAttr">
<xs:complexContent>
<xs:extension base="abstractKeyedItem">
<xs:sequence>
<xs:element name="kid" type="myAnyAttr" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:anyAttribute namespace="##other" processContents="skip"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="abstractKeyedItem" abstract="true">
<xs:sequence/>
<xs:attribute name="key" type="xs:string"/>
</xs:complexType>
</xs:schema>
Abstract class:
package xml.anyattr;
import javax.xml.bind.annotation.XmlAttribute;
public abstract class AbstractKeyedItem {
#XmlAttribute
public String key;
public AbstractKeyedItem() {}
}
Concrete class:
package xml.anyattr;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;
#XmlRootElement(name = "my-any-attr-root")
#XmlAccessorType(XmlAccessType.FIELD)
public class MyAnyAttr extends AbstractKeyedItem {
#XmlAnyAttribute
public Map<QName, String> attrMap = new HashMap<QName, String>();
#XmlElement(name = "kid")
public List<MyAnyAttr> myAnyAttrKids = new ArrayList<MyAnyAttr>();
public MyAnyAttr() {}
}
Utilities used to generate files:
package xml.anyattr;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.bind.SchemaOutputResolver;
import javax.xml.namespace.QName;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;
import org.apache.pdfbox.io.IOUtils;
public class MyAnyAttrUtils {
private static class MySchemaOutputResolver extends SchemaOutputResolver {
#Override
public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException {
new Throwable().printStackTrace(System.out);
File file = null;
if (namespaceUri.equals("")) {
file = XSD;
} else {
throw new IOException("Unsupported namespaceUri: " + namespaceUri);
}
StreamResult result = new StreamResult(file);
result.setSystemId(file.toURI().toURL().toString());
return result;
}
}
static File XSD = new File("target/test-classes/my-any-utils.xsd");
private static void generateXsd() throws JAXBException, IOException {
rm(XSD);
JAXBContext jaxbContext = JAXBContext.newInstance(MyAnyAttr.class);
SchemaOutputResolver outputResolver = new MySchemaOutputResolver();
jaxbContext.generateSchema(outputResolver);
InputStream is = new FileInputStream(XSD);
System.out.println("XSD:");
IOUtils.copy(is, System.out);
}
public static void main(String[] args) {
try {
printSimple();
generateXsd();
System.exit(0);
} catch (Exception e) {
e.printStackTrace(System.err);
System.exit(1);
}
}
private static void printSimple() throws JAXBException, PropertyException {
MyAnyAttr myAnyAttr = new MyAnyAttr();
myAnyAttr.key = "parent";
myAnyAttr.attrMap.put(new QName("oth", "dad"), "foo");
MyAnyAttr kid = new MyAnyAttr();
kid.key = "child";
kid.attrMap.put(new QName("oth", "kid"), "bar");
myAnyAttr.myAnyAttrKids.add(kid);
JAXBContext jaxbContext = JAXBContext.newInstance(MyAnyAttr.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
System.out.println("XML:");
marshaller.marshal(myAnyAttr, System.out);
System.out.println();
}
private static void rm(File file) throws IOException {
if (file.exists()) {
if (!file.delete()) {
throw new IOException("Failed to delete " + file);
}
}
}
}
Stack trace of Oracle implementation:
java.lang.Throwable
at xml.anyattr.MyAnyAttrUtils$MySchemaOutputResolver.createOutput(MyAnyAttrUtils.java:25)
at com.sun.xml.internal.bind.v2.schemagen.FoolProofResolver.createOutput(FoolProofResolver.java:55)
at com.sun.xml.internal.bind.v2.schemagen.XmlSchemaGenerator.write(XmlSchemaGenerator.java:462)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.generateSchema(JAXBContextImpl.java:794)
at xml.anyattr.MyAnyAttrUtils.generateXsd(MyAnyAttrUtils.java:44)
at xml.anyattr.MyAnyAttrUtils.main(MyAnyAttrUtils.java:53)
Stack trace of MOXy implementation:
java.lang.Throwable
at xml.anyattr.MyAnyAttrUtils$MySchemaOutputResolver.createOutput(MyAnyAttrUtils.java:25)
at org.eclipse.persistence.jaxb.compiler.SchemaGenerator.getSchemaForNamespace(SchemaGenerator.java:687)
at org.eclipse.persistence.jaxb.compiler.SchemaGenerator.addSchemaComponents(SchemaGenerator.java:177)
at org.eclipse.persistence.jaxb.compiler.SchemaGenerator.generateSchema(SchemaGenerator.java:154)
at org.eclipse.persistence.jaxb.compiler.SchemaGenerator.generateSchema(SchemaGenerator.java:142)
at org.eclipse.persistence.jaxb.compiler.Generator.generateSchemaFiles(Generator.java:221)
at org.eclipse.persistence.jaxb.JAXBContext.generateSchema(JAXBContext.java:405)
at org.eclipse.persistence.jaxb.JAXBContext.generateSchema(JAXBContext.java:353)
at xml.anyattr.MyAnyAttrUtils.generateXsd(MyAnyAttrUtils.java:44)
at xml.anyattr.MyAnyAttrUtils.main(MyAnyAttrUtils.java:53)

How to change a specific type's element name globally?

Let's say there is a class named
// with respect.
public class BlaiseDoughan {
}
I know I can change the element's name like this.
#XmlRootElement
public class MyRoot {
#XmlElement(name = "blaise-doughan") // I want this
private BlaiseDoughan blaiseDoughan
}
Is there any way to permanently set the target element name of BlaiseDoughan to blaise-doughan in every or any occurrence without name attribute?
I mean
#XmlRootElement
public class SomeOtherRoot {
#XmlElement // without the [name = "blaise-doughan"] part?
private BlaiseDoughan blaiseDoughan
}
Is there any package-level technique for this?
You could do:
Java Model
BlaiseDoughan
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="blaise-doughan")
public class BlaiseDoughan {
}
MyRoot
And then every reference to it could be mapped with #XmlElementRef.
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class MyRoot {
#XmlElementRef
private BlaiseDoughan blaiseDoughan;
public void setBlaiseDoughan(BlaiseDoughan blaiseDoughan) {
this.blaiseDoughan = blaiseDoughan;
}
}
Demo Code
Demo
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(MyRoot.class);
MyRoot myRoot = new MyRoot();
myRoot.setBlaiseDoughan(new BlaiseDoughan());
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(myRoot, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<myRoot>
<blaise-doughan/>
</myRoot>
Why This Works
#XmlElementRef corresponds to using ref in the element definition:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="blaiseDoughan"/>
<xsd:complexType name="myRoot">
<xsd:sequence>
<xsd:element ref="blaise-doughan"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="myRoot" type="myRoot"/>
<xsd:element name="blaise-doughan" type="blaiseDoughan"/>
</xsd:schema>

JAXB difference between #XmlSchemaType(name="normalizedString") and #XmlJavaTypeAdapter(NormalizedStringAdapter.class)

I'm writing a JAXB class without XSD-compilation.
Can anybody please tell me the difference between
#XmlSchemaType(name = "normalizedString")
private String normalized;
and
#XmlJavaTypeAdapter(NormalizedStringAdapter.class)
private String normalized;
?
#XmlSchemaType(name = "normalizedString")
private String normalized;
When the above annotation is used, the xsd:normalizedString type will be specified for the attribute/element corresponding to this property when an XML schema is generated.
#XmlJavaTypeAdapter(NormalizedStringAdapter.class)
private String normalized;
The NormalizedStringAdapter does the work during the unmarshal operation: line feeds, carriage returns, and tab characters are removed.
Example
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<adapter> A B
C </adapter>
<schemaType> A B
C </schemaType>
<control> A B
C </control>
</root>
Root
package forum383861;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;
#XmlRootElement
#XmlType(propOrder={"adapter", "schemaType", "control"})
public class Root {
private String adapter;
private String schemaType;
private String control;
#XmlJavaTypeAdapter(NormalizedStringAdapter.class)
public String getAdapter() {
return adapter;
}
public void setAdapter(String adpater) {
this.adapter = adpater;
}
#XmlSchemaType(name="normalizedString")
public String getSchemaType() {
return schemaType;
}
public void setSchemaType(String schemaType) {
this.schemaType = schemaType;
}
public String getControl() {
return control;
}
public void setControl(String control) {
this.control = control;
}
}
Demo
package forum383861;
import java.io.*;
import javax.xml.bind.*;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum383861/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
System.out.println();
jc.generateSchema(new SchemaOutputResolver() {
#Override
public Result createOutput(String arg0, String arg1) throws IOException {
StreamResult result = new StreamResult(System.out);
result.setSystemId(arg1);
return result;
}
});
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<adapter> A B C </adapter>
<schemaType> A B
C </schemaType>
<control> A B
C </control>
</root>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="root"/>
<xs:complexType name="root">
<xs:sequence>
<xs:element name="adapter" type="xs:string" minOccurs="0"/>
<xs:element name="schemaType" type="xs:normalizedString" minOccurs="0"/>
<xs:element name="control" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:schema>

How to disable Java Naming Conventions in xjc?

For example sOmE_PROPerty in xsd must be sOmE_PROPerty in java class not someProperty.
I tried to use globalBindings enableJavaNamingConventions="false" but it doesn't work.
You will need to use underscoreBinding="asCharInWord" instead of enableJavaNamingConventions="false":
customer.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
targetNamespace="http://www.example.org/customer"
xmlns="http://www.example.org/customer"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xsd:complexType name="customer">
<xsd:sequence>
<xsd:element name="sOmE_PROPerty" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
binding.xml
A JAXB binding file is used to customize the schema to Java conversion:
<jaxb:bindings
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:globalBindings underscoreBinding="asCharInWord"/>
</jaxb:bindings>
XJC Call
xjc -d out -b binding.xml customer.xsd
Customer
The generated property names now include the underscore character:
package org.example.customer;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "customer", propOrder = {
"sOmEPROPerty"
})
public class Customer {
#XmlElement(name = "sOmE_PROPerty", required = true)
protected String sOmEPROPerty;
public String getSOmE_PROPerty() {
return sOmEPROPerty;
}
public void setSOmE_PROPerty(String value) {
this.sOmEPROPerty = value;
}
}
Without Using binding.xml
If you instead make the following XJC call:
xjc -d out -customer.xsd
You will see that the generated properties do not include the underscore:
package org.example.customer;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "customer", propOrder = {
"sOmEPROPerty"
})
public class Customer {
#XmlElement(name = "sOmE_PROPerty", required = true)
protected String sOmEPROPerty;
public String getSOmEPROPerty() {
return sOmEPROPerty;
}
public void setSOmEPROPerty(String value) {
this.sOmEPROPerty = value;
}
}
Solved by changing source code of jaxb in class com.sun.xml.bind.api.impl.NameConverter like this:
public static final NameConverter standard = new Standard();
static class Standard extends NameUtil implements NameConverter {
public String toClassName(String s) {
return s;//toMixedCaseName(toWordList(s), true);
}
public String toVariableName(String s) {
return s;//toMixedCaseName(toWordList(s), false);
}
public String toInterfaceName( String token ) {
return token;//toClassName(token);
}
public String toPropertyName(String s) {
String prop = s;//toClassName(s);
// property name "Class" with collide with Object.getClass,
// so escape this.
if(prop.equals("Class"))
prop = "Clazz";
return prop;
}

Resources