JAXB generated classes: Ignore intermediate classes - jaxb

Here is a extract from my XML schema:
<xsd:complexType name="MyType">
<xsd:sequence>
<xsd:element name="Numbers">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Number" minOccurs="1" maxOccurs="5" type="xsd:decimal"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
JAXB generates me the following class:
public class MyType {
protected MyType.Numbers numbers;
public static class Numbers {
protected List<BigDecimal> number;
}
}
But I'd like to ignore this intermediate class and have something like:
public class MyType {
protected List<BigDecimal> number;
}
Is that somehow possible?

Yes, that is possible with JAXB only with the help of external plugins, as this modification is actually change of the model. Have a look at #XmlElementWrapper plugin.
Note: The same question was already asked on this forum (How generate XMLElementWrapper annotation, Dealing with JAXB Collections, JAXB List Tag creating inner class). Please, use search first.

Related

WSDL2Java: missing complex element type generates java.lang.Object argument types

I am trying to import this WSDL: https://gateway.monster.com:8443/bgwBroker
Among others, it includes this XSD: http://schemas.monster.com/current/xsd/Query.xsd, which contains this snippet:
<xsd:element name="Query">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Target" type="xsd:string"/>
<xsd:element name="SubTarget" type="xsd:string" minOccurs="0"/>
<xsd:element name="ResumeRestriction" minOccurs="0"/>
<xsd:element name="ReturnRestriction" minOccurs="0" maxOccurs="unbounded">
...
</xsd:element>
<xsd:element name="SelectBy" minOccurs="0" maxOccurs="unbounded">
...
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
...
<xsd:element name="ResumeRestriction">
...
</xsd:element>
As you can see, complex type ResumeRestriction and its fields is defined at the bottom, but referenced inside Query. There is a reference missing here. ReturnRestriction and SelectBy are defined inline and are generated correctly.
Using WSDL2Java, this generates the following annotated classes:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"target",
"subTarget",
"resumeRestriction",
"returnRestriction",
"selectBy"
})
#XmlRootElement(name = "Query")
public class Query {
#XmlElement(name = "Target", required = true)
protected String target;
#XmlElement(name = "SubTarget")
protected String subTarget;
#XmlElement(name = "ResumeRestriction")
protected Object resumeRestriction;
...
public void setResumeRestriction(Object value) {
this.resumeRestriction = value;
}
}
Making resumeRestriction of type Object instead of the right type.
If do have a generated version of ResumeRestriction just fine. They are just not being tied together:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"storeRenderedTextResume",
"doNotRenderSid",
"activeOnBoard"
})
#XmlRootElement(name = "ResumeRestriction")
public class ResumeRestriction {
...
}
If I now create a request using Spring Web Services, I cannot use the ResumeRestriction class, which I need in my request to set a specific flag.
ObjectFactory objectFactory = new ObjectFactory();
Query query = objectFactory.createQuery();
query.setTarget("JobSeekers");
ResumeRestriction resumeRestriction = objectFactory.createResumeRestriction();
resumeRestriction.setStoreRenderedTextResume(true);
query.setResumeRestriction(resumeRestriction);
getWebServiceTemplate().marshalSendAndReceive("https://gateway.monster.com:8443/bgwBroker", query);
This will throw the following error:
[com.sun.istack.SAXException2: Instance of "com.monster.schemas.monster.ResumeRestriction" is substituting "java.lang.Object", but "com.monster.schemas.monster.ResumeRestriction" is bound to an anonymous type.]
How can I solve this problem?
I obviously cannot change the WSDL or XSD, as I pull those in remotely. Is this is a bug on their side and if so, can I work around it?
You could specify the class reference in JAXB bindings file:
<jxb:bindings
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
version="2.1"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jxb:extensionBindingPrefixes="xjc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemalocation="http://java.sun.com/xml/ns/jaxb
http://java.sun.com/xml/ns/jaxb"
>
<jxb:bindings >
<jxb:globalBindings typesafeEnumMaxMembers="3000"/>
</jxb:bindings>
<jxb:bindings schemaLocation="http://schemas.monster.com/current/xsd/Query.xsd">
<jxb:bindings node="//xs:element[#name='Query']//xs:element[#name='ResumeRestriction']">
<jxb:class ref="com.monster.schemas.monster.ResumeRestriction"/>
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>

JAXB XmlAdapter via bindings file

Consider this XSD:
<xsd:schema targetNamespace="http://foobar" xmlns:tns="http://foobar"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="IdAttribute">
<xsd:attribute name="id" type="xsd:token" />
</xsd:complexType>
<xsd:complexType name="FoobarType">
<xsd:sequence>
<xsd:element name="someIds" type="tns:IdAttribute" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="Foobar" type="tns:FoobarType" />
</xsd:schema>
which results in the following, generated Java class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "FoobarType", propOrder = {"someIds"})
public class FoobarType {
#XmlElement(required = true)
protected List<IdAttribute> someIds;
// ...
}
Because IdAttribute only contains one String (id), I want to map these Strings directly into my FoobarType for easier usage. Therefore I wrote an XmlAdapter:
public class IdAttributeAdapter extends XmlAdapter<IdAttribute, String> { ... }
I've edited the generated class manually to verify my XmlAdapter works as expected (it does):
#XmlElement(required = true)
#XmlJavaTypeAdapter(IdAttributeAdapter.class)
protected List<String> someIds;
Of course I want to have this little code change done during code generation, so I've added the following bindings file (src/main/xjb/bindings.xjb):
Attempt 1:
<jaxb:bindings version="2.0" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd">
<jaxb:bindings schemaLocation="../resources/xsd/foobar.xsd">
<jaxb:bindings node="//xsd:complexType[#name='IdAttribute']">
<xjc:javaType
name="java.lang.String"
adapter="org.foobar.IdAttributeAdapter" />
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
Result
com.sun.istack.SAXParseException2; systemId: file:/C:/dev/foobar/src/main/xjb/bindings.xjb;
lineNumber: 12; columnNumber: 91;
compiler was unable to honor this conversion customization. It is attached to a wrong place, or its inconsistent with other bindings.
Attempt 2 (from here):
<jaxb:globalBindings xmlns:foo="http://foobar" xsi:schemaLocation="http://foobar ../resources/xsd/foobar.xsd">
<xjc:javaType
name="java.lang.String"
xmlType="foo:IdAttribute"
adapter="org.foobar.IdAttributeAdapter" />
</jaxb:globalBindings>
Result
com.sun.istack.SAXParseException2; systemId: file:/C:/dev/projects/luna/oasch/src/main/xjb/bindings.xjb;
lineNumber: 8; columnNumber: 112;
undefined simple type "{http://foobar}IdAttribute".
I've tried a few variations, but they all resulted in similar errors. So what's the correct way to add an XmlApender using a bindings file?
What you try to do is wrong.
You should replace fully the xml type IdAttribute with the java type String.
You will have to work with generic dom.Element type and convert it <-> String.
Here is an exemple : JAXB #XmlAdapter for arbitrary XML for your case replace the java type Bar with String.
Other solution would be to add behavior to the generated class so that you add methods to your FoobarType to work as if it has a List of String
e.g. add methods
List<String> getIds()
void setIds(List<String>)
Here is a link that explain how you can do this : "Adding behavior"

generated XSD From Java use moxy

#XmlRootElement
public class OtherClass {
#XmlAttribute
private String other;
}
#XmlRootElement
public class Simple extends OtherClass {
#XmlAttribute
private String id;
#XmlValue
public String contents;
}
JAXBContext context = JAXBContext.newInstance(OtherClass.class,Simple.class);
System.out.println(context);
System.out.println("org.eclipse.persistence.Version:"+Version.getVersionString());
context.generateSchema(new MySchemaOutputResolver());
System.out.println(sw);
Use woxy generated XSD results
org.eclipse.persistence.jaxb.JAXBContext#1292d26
org.eclipse.persistence.Version:2.5.0.v20130425-368d603
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="otherClass">
<xsd:sequence/>
<xsd:attribute name="other" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="simple">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="id" type="xsd:string"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:element name="otherClass" type="otherClass"/>
<xsd:element name="simple" type="simple"/>
</xsd:schema>
sample/other no inheritance in xsd, but there is in java
Excuse me, if to solve the problem? Here I thank you first
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
What the Mappings Mean
With MOXy as your JAXB provider (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html) you can use the folllowing code:
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Simple.class, OtherClass.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum18029865/input.xml");
Simple simple = (Simple) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(simple, System.out);
}
}
to convert the following XML to/from your Java objects:
<?xml version="1.0" encoding="UTF-8"?>
<simple id="foo" other="bar">Hello World</simple>
How the Mappings Relate to XML Schema
The classes as you have mapped them don't have a real representation in XML schema. Since you have used the #XmlValue annotation the resulting type will extend xsd:string, but you have also indicated that it should extend the complex type corresponding to OtherClass.
What the JAXB Reference Implemenetation Does
For the exact same mappings the JAXB RI will through an exception when creating the JAXBContext. Since this representation is not allowed in the XML schema, they decide not to allow it in the mappings. I prefer the option we chose in MOXy where still support the mapping.
Exception in thread "main" com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
#XmlValue is not allowed on a class that derives another class.
this problem is related to the following location:
at public java.lang.String forum18029865.Simple.contents
at forum18029865.Simple
at com.sun.xml.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:102)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:472)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:302)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1140)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:154)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:121)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:248)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:235)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:432)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:637)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:584)
at forum18029865.Demo.main(Demo.java:9)

Equivalent to #XmlElement.required in #XmlPath in EclipseLink MOXy

How to implement #XmlElement.required flag using #XmlPath annotation in EclipseLink MOXy 2.4.1 version?
You can use the #XmlElement(required=true) along with the #XmlPath annotation to specify that the leaf element is required.
Customer
Below is a sample domain model with two fields mapped with #XmlPath on one of them I've also used #XmlElement(required=true).
package forum13854920;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
#XmlPath("personal-info/first-name/text()")
private String firstName;
#XmlPath("personal-info/last-name/text()")
#XmlElement(required=true)
private String lastName;
}
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
XML Schema
Below is the XML schema that corresponds to the domain model. Note how the last-name element does not have minOccurs="0".
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="customer">
<xsd:sequence>
<xsd:element name="personal-info" minOccurs="0">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="first-name" type="xsd:string" minOccurs="0"/>
<xsd:element name="last-name" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
Demo
The following demo code can be used to generate the XML schema.
package forum13854920;
import java.io.IOException;
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(Customer.class);
jc.generateSchema(new SchemaOutputResolver() {
#Override
public Result createOutput(String namespaceURI, String suggestedFileName) throws IOException {
StreamResult result = new StreamResult(System.out);
result.setSystemId(suggestedFileName);
return result;
}
});
}
}
Currently EclipseLink JAXB (MOXy) does not have the equivalent of the required property on the #XmlElement annotation for the other segments of the path. If you are interested in this behaviour please enter an enhancement request using the link below:
https://bugs.eclipse.org/bugs/enter_bug.cgi?product=EclipseLink

WebService Client and List<JAXBElement<?>>

When I try to generate a client from a wsdl document, I get a client that seems to have a lot of JAXBElement atributes, for instance
protected List<JAXBElement<?>> nameOrLinkingNameOrFamilyName;
I use soapUI to generate with apache cxf 2.3.3 as tool, also as config file the following:
<jaxb:bindings version="2.1"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jaxb:globalBindings generateElementProperty="false"/>
</jaxb:bindings>
As far as I saw this is related with the choice tags in the wsdl document.
thanks in advance
A JAXBElement will be generated for choice properties where multiple XML elements will correspond to the same Java class. This is in order to preserve information about the element since this can not be derived from the type of the value.
binding.xml
The following JAXB schema bindings file will ensure that a choice property is generated:
<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<globalBindings choiceContentProperty="true"/>
</bindings>
XML Schema That Will Produce Object Property
In this version of the XML schema, all of the XML elements will correspond to a different Java class:
<xsd:choice>
<xsd:element name="address" type="address"/>
<xsd:element name="phone-number" type="phoneNumber"/>
<xsd:element name="note" type="xsd:string"/>
</xsd:choice>
Since the value of the choice property is sufficient to uniquely identify the element, the property does not contain a JAXBElement to preserve this information:
#XmlElements({
#XmlElement(name = "address", type = Address.class),
#XmlElement(name = "phone-number", type = PhoneNumber.class),
#XmlElement(name = "note", type = String.class)
})
protected Object addressOrPhoneNumberOrNote;
XML Schema That Will Produce JAXBElement Property
Now we will modify the choice structure so that both the note and email methods will correspond to the String class.
<xsd:choice>
<xsd:element name="address" type="address"/>
<xsd:element name="phone-number" type="phoneNumber"/>
<xsd:element name="note" type="xsd:string"/>
<xsd:element name="email" type="xsd:string"/>
</xsd:choice>
Since the value of the choice property is no longer sufficient to uniquely identify the element, the property must contain a JAXBElement to preserve this information:
#XmlElementRefs({
#XmlElementRef(name = "phone-number", type = JAXBElement.class),
#XmlElementRef(name = "email", type = JAXBElement.class),
#XmlElementRef(name = "address", type = JAXBElement.class),
#XmlElementRef(name = "note", type = JAXBElement.class)
})
protected JAXBElement<?> addressOrPhoneNumberOrNote;
For More Information
http://blog.bdoughan.com/2011/04/xml-schema-to-java-xsd-choice.html

Resources