How to generate JAXB classes using XJC using some existing Java classes? - java

The scenario is I have a bunch of schemas in .xsd format, which I can generate using XJC. However, I do not like one of the class generated using this approach, as a result, I would like to manually curate a replacement for that specific class. That class is being referenced by other classes in the schema. Is there a way of doing that?

You can use an external binding file to configure XJC to do what you want. In the example below the existing class com.example.Foo will be used for the complex type named Foo.
binding.xml
<jxb:bindings schemaLocation="yourSchema.xsd">
<jxb:bindings node="//xs:complexType[#name='Foo']">
<jxb:class ref="com.example.Foo"/>
</jxb:bindings>
</jxb:bindings>
XJC Call
xjc -d outputDir -b binding.xml yourSchema.xsd

You can manually create the class you have to use jaxb annotaion from javax.xml.bind.annotation package in you class.
below is the link for details of the same.
http://docs.oracle.com/javaee/5/api/javax/xml/bind/annotation/package-summary.html
but if you can be more specific to your question like what you didnt like in autogenerated classes like class name or package name or anything else that will be a great help to answer this question.

As long as you are annotating the fields/properties with the same values, it is ok to manually change your class and also change any references(including the ObjectFactory class).

Related

Generate wrapper for the Jaxb generated ObjectFactory methods nillable=true, minOccurs=0

I am using JAXB, I have xsd files which I cannot modify which has elements with
minOccurs="0" nillable="true"
Which results that in the generated ObjectFactory class I have a lot of code like:
JAXBElement<SomeObject>
Then the code, that I have to write to both marshall and unmarshall is boilerplate and ugly.
Is there a way to generate somehow automaticaly another layer of abstraction so that I would have some other ObjectFactory class as well, but it would not operate on JAXBElement but instead would have it wrapped in some methods? Like it is when in the binding.xjb you specify the following:
<jaxb:globalBindings localScoping="toplevel" generateElementProperty="false">
which is unfortunately not an option for me.

How to use JAXBElement in jackson xml serialization?

I have autogenerated java classes from xsd using xsd2java. I cannot modify neither xsd nor the java classes.
Problem: in one class an element of List<JAXBElement> is generated.
If I now add any JAXBElement, the jackson xml marshaller won't show the proper xml element, but the properties of the JAXBElement serialized. Like declaredType, scope, etc. See below.
#XmlRootElement(name = "bookingRequest")
public class AutogeneratedReq {
private List<JAXBElement<?>> someElements;
}
Usage:
AutogeneratedReq req = new AutogeneratedReq();
JAXBElement<?> person = new ObjectFactory().createPerson();
req.getSomeElements().add(person);
Result:
<someElements>
<JAXBElement>
<name>person</name>
<declaredType>net.some.company.Person</declaredType>
<scope>net.some.company</scope><value someattribues="test"/>
<nil>false</nil>
<globalScope>false</globalScope>
<typeSubstituted>false</typeSubstituted>
</JAXBElement>
</someElements>
Question: how can I tell jackson or spring-mvc to generate proper xml, and not JAXBElement serialization explicit?
I don't know which xsd2java utility you currently use, but you can try the following maven plugin to generate Java classes from XSD files.
https://github.com/highsource/jaxb2-basics/wiki/Using-JAXB2-Basics-Plugins
And then you can use following extension to create correctly typed POJO's.
https://github.com/highsource/jaxb2-basics/wiki/JAXB2-Simplify-Plugin
BUT even if you can create typed POJO attributes, the XML file generated from this POJO may not be 100% valid against original XSD file.
<jaxb:bindings multiple="true" node="//xs:element[#name='someElement']//xs:complexType//xs:choice//xs:element">
<simplify:as-element-property/>
</jaxb:bindings>

wsimport: adding Binding when the XSD is embedded in WSDL?

I'm trying to generate some java code from the following WSDL: http://www.ebi.ac.uk/Tools/services/soap/emboss_needle?wsdl
$ wsimport -keep "http://www.ebi.ac.uk/Tools/services/soap/emboss_needle?wsdl"
however it generates some JAXBElement<String> instead of String. So I've tried to use a xjb binding as it is described here: Get rid of JAXBElement in classes generated by wsimport called from ant
<jxb:bindings xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<jxb:bindings>
<jxb:globalbindings generateelementproperty="false">
<jxb:javatype name="java.lang.String" xmltype="xs:string"/>
</jxb:globalbindings>
</jxb:bindings>
</jxb:bindings>
but wsimport raises an exception:
$ wsimport -keep -b binding.xjb "http://www.ebi.ac.uk/Tools/services/soap/emboss_needle?wsdl"
[ERROR] The "jxb:globalbindings" customization is not associated with any schema element.
line 6 of file:/home/lindenb/tmp/WS/biostar14996.xjb
The XSD schema is embedded in the WSDL document. What URI should I give for the jxb:schemaLocation ? How can I fix that problem ?
Thanks,
P.
Eventually I ended up with:
<jxb:bindings version="2.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema" wsdlLocation="YOUR_WSDL_LOCATION">
<jxb:globalBindings generateElementProperty="false"/>
</jxb:bindings>
The EMBL-EBI's EMBOSS needle service (http://www.ebi.ac.uk/Tools/services/soap/emboss_needle?wsdl), and most of their other analysis tool services (see http://www.ebi.ac.uk/Tools/webservices/) allow for submission parameters to have three states:
Explicit value
Null value
To be omitted
This provides compatibility with a wide range of SOAP tool-kits, some of which assume only one or two out of these three behaviors.
An unfortunate side-effect of this is that tool-kits such as JAX-WS, which understand that there are three states, need to use a more complex representation to handle this. Thus the JAXBElement classes are required. EMBL-EBI provides sample clients with source code using JAX-WS for their InterProScan (SOAP) and NCBI BLAST (SOAP) services, which use the same pattern for their parameters (see http://www.ebi.ac.uk/Tools/webservices/tutorials/06_programming/java/soap/jax-ws).
I believe your problem is similar to this post; there are links pointing you to the documentation, as well as the solution; basically, when you bind against WSDL files, you need a different top level element; the accepted response gives you the command line as well.

How to detect required XML attributes?

I have an XML content without defined attributes, like this:
<rootElement>
<subElement1/>
</rootElement>
I want to populate this XML content with required attributes defined in XML Schema (XSD) for this XML.
For example, according to XSD subElement1 has required attribute 'id'.
What is the best way (for Java processing) to detect that and add such attributes to XML?
We need to add required attributes and set appropriate values for them.
As a result for example above we need to have the following XML:
<rootElement>
<subElement1 id="some-value"/>
</rootElement>
In the XML schema definition, i.e. XSD file, attributes are optional by default. To make an attribute required, you have to define:
<xs:attribute name="surname" type="xs:string" use="required"/>
You will find a very good introduction on XML and XML Schema Definitions, i.e. XSD, on W3 Schools.
In Java the equivalent of defining a XML schema is using JAXB, i.e. Java API for XML Binding that is included into Java SE. There you would define, e.g.
#XmlRootElement
public class Person { public #XmlAttribute(required=true) String surname; }
Hope this could clarify your question.
I would suggest you to use JAXB for that. Search the Internet for tutorials.
Steps to proceed further with JAXB,
Generate Java files using JAXB by providing the schema
Unmarshal your XML to generated Java classes (beans). Don't do validation or set validation handler here.
Populate those classes with appropriate values. required elements can be found using annotation look up. JAXB annotation for element would look like something, #XmlElement(name = "ElementName", required = true). And an attribute annotation would be something similar to this, #XmlAttribute(required = true)
Marshal your bean back to XML. You can validate your bean using ValidationHandler, while marshalling. Below is the sample code snippet,
marshller = JAXBContext.newInstance(pkgOrClassName).createUnmarshaller();
marshller.setSchema(getSchema(xsd)); // skip this line for unmarshaller
marshller.setEventHandler(new ValidationHandler()); // skip this line for unmarshaller
Use a DOM parser.Has methods to traverse XML trees, access, insert, and delete nodes
I have had the same idea of Cris but I think that with this validator you don't have information about the point in which you have had the error.
I think that you have to create or extend your own validator.

JAXB workaround for Chameleon XSD imports?

this is my first question, so please be gentle ;)
I'm stuck with a weird problem. Essentially i get three XSD definitions like the following:
PartA.xsd
targetNameSpace="PartA"
include="PartB.xsd"
PartB.xsd
<!-- no namespace definition!!! -->
PartC.xsd
targetNameSpace="PartC"
inlude="PartB.xsd"
import="PartA.xsd"
The error pops up, when binding PartC via JAXB to Java classes:
A class/interface with the same name "b.exampleType" is already in use. Use a class customization to resolve this conflict.
This confusing error happened most likely because the schema uses a technique called "chameleon schema", which causes a single definition to be loaded multiple times into different namespaces. See http://forums.java.net/jive/thread.jspa?threadID=18631 for more about this.
Following the link, i found out, the actual error lies in PartB, which has no namespace declaration! This method is called Chameleon Schema. The defined types in PartB will adopt the namesspace of the importing XSD.
So in my case, there are two namespaces for the same type:
"PartA"
"PartC"
And this is, where JAXB breaks down. I haven't found a way to bind PartC properly. And (to make things tricky) i have chance to change the original XSD definitions!
Has anyone come across this phenomena or something like that before and has a valid workaround for it?
I was facing the same problem using wsdl2java:
WSDLToJava Error: Thrown by JAXB : A class/interface with the same
name "Respuesta" is already in use. Use a class customization to
resolve this conflict.
But this question pointed me in the right direction.
Using wsdl2java from CFX you can customize how elements are binded to classes using a binding.xml file.
For example:
/Applications/apache-cxf-2.7.13/bin/wsdl2java -b ./src/main/resources/binding.xml -V -d src/main/java -compile -classdir target/classes http://someurl.wsdl
The key is to explain in the binding.xml to name certain xsd element with one especific className to avoid colissions:
<jxb:bindings xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" version="2.1">
<jxb:bindings schemaLocation="./someXsdFile.xsd">
<!-- Rename the respuesta class to resolve a naming conflict with other Respuesta element already defined-->
<jxb:bindings node="//xs:element[#name='respuesta']/xs:complexType">
<jxb:class name="Respuesta2" />
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>
Hope this helps to the next person with this problem using wsdl2java. I supose that other tools should allow similar aproaches to this problem.
I was having the same issue and the google search landed me here.
Your question is detailed enough and I was able to find the answer,
what I did is put the namespace in PartB.xsd and use XJC to generate the java classes.
I added the following:
xmlns:ns="http://www.myCompany.com/2009/01/CustSchema" targetNamespace="http://www.myCompany.com/2009/01/CustSchema"
The following is available, although it does not provide a lot of detail:
https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#compiling-xml-schema-how-modularization-of-schema-interacts-with-xjc
There is one workaround for this problem. More specifically what you have to do is:
1.Define proxy schema (PartB_proxy.xsd) for PartB.xsd (with no namespace definition) and specify temporary namespace (for example PartB):
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" xmlns="PartB"
targetNamespace="PartB">
<xsd:include schemaLocation="partB.xsd"/>
2.Prepare bindings file (for large schemas this step could be automatized by generating episode file - more details in my article - I've attached link at the end of this post) - this step is required to avoid generating duplicate classes for PartB schema (by default the duplicate classes that come from PartB schema will be generated for PartA and PartC schemas/java packages):
<bindings version="2.1" xmlns="http://java.sun.com/xml/ns/jaxb"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<bindings scd="x-schema::partA" xmlns:partA="PartA">
<bindings scd="~partA:SomeType1">
<class ref="SomeType1"/>
</bindings>
<bindings scd="~partA:SomeType2">
<class ref="SomeType2"/>
</bindings>
</bindings>
<bindings scd="x-schema::partC" xmlns:partC="PartC">
<bindings scd="~partC:SomeType1">
<class ref="SomeType1"/>
</bindings>
<bindings scd="~partC:SomeType2">
<class ref="SomeType2"/>
</bindings>
</bindings>
</bindings>
3.Generate JAXB classes using XJC, example Gradle task (I am using Moxy implemetnation but You can use default XJC generator from JDK 8 or standalone JAXB library for newer JDK versions):
task generateExampleChameleonSourcesWithMoxy(type: JavaExec) {
classpath = configurations.moxy
main = 'org.eclipse.persistence.jaxb.xjc.MOXyXJC'
args '-b'
args 'src/main/resources/example/chameleon/bindings.xml'
args '-d'
args 'src/main/generated'
args 'src/main/resources/example/chameleon/PartA.xsd'
args 'src/main/resources/example/chameleon/PartB_proxy.xsd'
args 'src/main/resources/example/chameleon/PartC.xsd'
}
4.Remove proxy namespace (partB) from package-info.java generated for partB schema:
javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package partB;
5.Finally You can marshal and unmarshal these XML documents using the Default Namespace Remapping technique by:
defining the default namespace for the types with no namespace:
com.sun.xml.internal.bind.defaultNamespaceRemap property (JDK JAXB >=JDK 1.8)
com.sun.xml.bind.defaultNamespaceRemap property (External JAXB JDK 1.9+)
performing marshalling/unmarshalling as usual
Here’s the code that is responsible for that (JDK 1.8):
//Initialize JaxbContext
Map<String, String> properties = new HashMap<>();
properties.put("com.sun.xml.internal.bind.defaultNamespaceRemap", "partC");
JAXBContext jaxbContext = JAXBContext.newInstance(new Class[]{SomeType1.class}, properties);
//Unmarshall Message object file from example XML
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Object messageObject = unmarshaller.unmarshal(getClass().getResourceAsStream("/example/chameleon/Division.xml"));
//Marshall message object back into XML
ByteArrayOutputStream xmlMessageOS = new ByteArrayOutputStream();
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(messageObject, xmlMessageOS);
System.out.println(xmlMessageOS.toString());
//Validate output XML with schema
InputStream xmlInputStream = new ByteArrayInputStream(xmlMessageOS.toByteArray());
Source xmlSource = new StreamSource(xmlInputStream);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(getClass().getResource("/example/chameleon/PartC.xsd"));
Validator validator = schema.newValidator();
validator.validate(xmlSource);`
Unfortunately, You have to use different JAXB contexts for JAXB objects coming from PartA and PartC namespaces. This is required because JAXB default namespace remapping property is set per JAXB context - this way JAXB marshaller/unmarshaller knows how to deal with POJO objects that have no namespace defined (PartB).
Nevertheless, I do not think it is problematic as usually all the JAXB contexts are initialized once - during the application startup (in singleton).
To sum up, the workaround described above allows You to use JAXB to marshall/unmarshall XML documents without modifying the chameleon schemas. The proxy XSD file is used only at the beginning - for the process of generating JAXB objects (XJC). The marshalled/unmarshalled XML documents still conform to the original schemas. This technique is useful especially if You are not allowed to modify the schemas. I was dealing with that problem for read-only NDC standard (set of schemas) in 18.1 version defined by http://www.iata.org.
I've written an article on that topic: How to deal with Chameleon namespace design in JAXB. You will find it here: https://medium.com/#pziobron/how-to-deal-with-chameleon-namespace-design-in-jaxb-e36bcc03767d. Hopefully, You will find it useful.

Categories

Resources