schemagen: How to share classes, but not their namespaces? - java

I have two top-level classes which share a third class by composition. Example:
#XmlRootElement
#XmlType(namespace = "http://example.com/foo")
public class Foo {
public Shared shared;
}
#XmlRootElement
#XmlType(namespace = "http://example.com/bar")
public class Bar {
public Shared shared;
}
public class Shared {
public String string;
}
Each of these classes is assigned to a different package in a different compilation unit (module).
Now when I use schemagen on each top level class, I would like the Shared class to have the same name space than the top level class. So the output for Foo should look like this:
<?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="foo" type="Foo"/>
<xs:complexType name="Foo">
<xs:sequence>
<xs:element name="shared" type="Shared" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Shared">
<xs:sequence>
<xs:element name="string" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
However, it doesn't work like this. Instead the Shared class has the default namespace and so I get two schema files, one for the namespace of Foo and one for the namespace of Shared.
Is there a way to fix this without the obvious solution to duplicate the Shared class and thus, not sharing it anymore?

If the shared class should have the same name space as the top level class you have to duplicate it. If you really want to share it, it must be defined in a third XSD and imported into the two top-level XSDs. So the result for Foo should rather look like:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.example.org/schema/foo"
xmlns:shared="http://www.example.org/schema/shared"
targetNamespace="http://www.example.org/schema/foo">
<xs:import namespace="http://www.example.org/schema/shared" schemaLocation="http://www.example.org/schema/shared/shared.xsd"/>
<xs:element name="foo" type="Foo"/>
<xs:complexType name="Foo">
<xs:sequence>
<xs:element name="shared" type="shared:Shared" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
The XSD for Shared can then be imported into both top level XSDs. If your Shared XSD is located in a separate project you might need a catalog file to specify its location:
PUBLIC "http://www.example.org/schema/shared" "../../../../shared/src/main/xsd/shared.xsd"
I am always using contract-first, i.e. create the XSD and let JAXB generate the classes. You are using schemagen to generate the XSD based on the classes. Just in case you are using the jaxb2-maven-plugin I found a Parameter transformSchemas which sounds like it is doing what you need. It lets you specify schema mappings and writes xs:import statements into the resulting XSD. I did not try it, but I hope it helps.

After investigating the issue and elaborating the options, I have decided to change the design and use one and only one namespace for all my classes. Here's the rationale:
The relationship between a class and a namespace is naturally one to one. Even if I manually mock-up an XSD so that my instance document properly validates, the JAXB unmarshaller ignores elements which belong to a name space which doesn't match, so hacking the XSD doesn't help at all.
I can easily reuse the same namespace over multiple packages and compilation units. The class path just needs to be set up correctly, which Maven will do for me when using the jaxb2-maven-plugin.
I can easily associate all my classes with one namespace and still have different XSD files for each top-level element. The advantage is that each output XSD file contains only the classes (i.e. complex types) which are referenced from the included root element classes when running schemagen.
After making this change, I get one XSD file per compilation unit. Each compilation unit contains exactly one root element (i.e. class). Each root element references only the complex types which are strongly reachable through the class representing the root element. This is exactly what I wanted.

Related

How to add extra methods in generated classes using jaxb-xjc schema

Is there's a way to have extra methods in an class generated with JAXB ... To be more specific I would like make changes in my .xsd files and not code some methods in a "first.java" class and then make my .xsd generated classes inherit from "first.java" .. My goal is to modify only my .jaxb file .. so can we create methods with .xsd ?
I have an ".xsd" file that generates a class. And I want to know if it's possible to add to this generated class some other methods besides the getters and setters.
I search around tutorials and some StackOverflow topics but I didn't find instructions on how can I add custom methods to a generated class described on XML Schema.
Here's my .xsd file :
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb" jxb:version="2.1">
<xs:element name="Myclass" />
<xs:complexType name="Myclass">
<xs:complexContent>
</xs:complexContent>
</xs:complexType>
</xs:schema>
And here's the class generated with this .xsd file :
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Myclass")
public class Myclass
{
}
Here's an example of a class that I want to generate with
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Myclass")
public class Myclass
{
public String method(){
return "Hello";
}
}
Thank's in advance :)
If you receive the XSDs from a client and they can change often, I would recommend not making changes to the XSDs as a design decision. I would recommend the java route as mentioned on the JAXB documentation:
https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#compiling-xml-schema-adding-behaviors

A message body writer for Java type, class bookInfoListType, and MIME media type application/xml was not found

I have been browsing stackoverflow for this error lately and I'm unable to find a solution on almost all the threads that I have been to. That's why I'm posting this question here.
The problem is that I'm having said error while returning the response. Here is my XSD definition:
<xs:element name="bookInfoList">
<xs:complexType>
<xs:sequence>
<xs:element name="bookInfo" type="bookInfoType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="bookInfo" type="bookInfoType"/>
<xs:complexType name="bookInfoListType">
<xs:sequence>
<xs:element name="bookInfo" type="bookInfoType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="bookInfoType">
<xs:sequence>
<xs:element name="bookId" type="xs:string" minOccurs="1" maxOccurs="1"/>
<!-- ... more elements !-->
</xs:sequence>
</xs:complexType>
which generated the following bookListInfoType class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "bookInfoListType", propOrder = {
"bookInfo"
})
public class bookInfoListType {
protected List<bookInfoType> bookInfo;
public List<bookInfoType> getbookInfo() {
if (bookInfo == null) {
bookInfo = new ArrayList<bookInfoType>();
}
return this.bookInfo;
}
}
But when I try to send the response back like
return Response.status(HttpStatus.SC_OK).entity(bookInfoListConverter.convert(wsContext, allBooks)).build();
I get the said error.
In my ObjectFactory, I do see
public bookInfoListType createbookInfoListType() {
return new bookInfoListType();
}
Here is my REST method:
#Path ("v1/storename/")
public class BookResource
{
#GET
#Path ("books/{book_id}/info.xml")
#Produces (MediaType.APPLICATION_XML)
public Response getBookInfoXML () {
//business logic
return Response.status(HttpStatus.SC_OK).entity(bookInfoListConverter.convert(wsContext, allBooks)).build();
}
}
Not to mention, I have another JAXB generated class called bookInfoList as you can see in the XSD. Their definition is almost identical (this is one thing I'm suspecting which could be the problem, but rest of my classes which are following same pattern are working fine).
I am still trying to bang my head around to figure out whats going on but I'm running out of ideas, any help would be appreciated.
An entity returned from the resource method in the form of an arbitrary Java object can be serialized by Jersey into a container output stream as a specified representation. Of course, while JAX-RS implementations do provide default support for most common combinations of Java type and it's respective on-the-wire representation formats, JAX-RS implementations do not support the conversion described above for any arbitrary Java type and any arbitrary representation format by default.
JAX-RS Entity Providers
For every Java type and content type combination your application uses (which is not covered by the default providers that come with the JAX-RS implementation) you must have a provider that knows how to handle the combination, so you must have appropriate MessageBodyWriter and MessageBodyReader classes (see the link above for how to write them if you should need to - you might want to refer to the exact documentation for the JAX-RS implementation/version you are using).
With that said, I find your error a little bit strange since Jersey already contains default support for entity providers that can marshall JAXB beans. You might want to compare your service against a tutorial (e.g. Using JAX-RS With JAXB) to make sure the error you posted is not a red herring

Change WSDL xsd:complexType name with Apache CXF

I use Apache CXF to publish a webservice, generating the WSDL "on-the-fly". This works great, but I would like to change the naming convention of the generated types. As the service clients (C#) generate code based on the WSDL, the default xsd:complexType naming results in type names starting with lower-case letters.
Here is an excerpt from the generated WSDL:
<xs:complexType name="protocolItem">
<xs:sequence>
<xs:element minOccurs="0" name="data" type="tns:protocolItemData"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="elements" nillable="true" type="tns:protocolElement"/>
<xs:element minOccurs="0" name="meta" type="tns:protocolItemMeta"/>
</xs:sequence>
</xs:complexType>
This is the Java code that results in the above WSDL fragment:
#RooJavaBean
public class ProtocolItem {
private ProtocolItemData data;
private ProtocolItemMeta meta;
private List<ProtocolElement> elements;
}
How can I change the generated WSDL to use something like <xs:complexType name="ProtocolItem">?
Hopefully I am not missing a obvious annotation... Thanks!
EDIT: thanks for the first answer! So there is a way to do this "per class" - can I configure the CXF naming conventions? Would be nice if I did not need to annotate all classes.
Try this:
#XmlType(name="ProtocolItem")
public class ProtocolItem {
...
}
Hope this helps.
Using the Aegis data binding instead of the JAXB data binding with Apache CXF helped: This changed the naming convention. Unfortunately, I realised this after having added all #XmlType annotations, so I removed them again...
I stumbled over Aegis when searching for a solution for collections/arrays - they are not wrapped with JAXB, but with Aegis, which is required for proper client code generation (typed collections instead of arrays).

How can I add WSDL <types> restrictions programmatically?

Where I work, we have a policy in place that states we should try to build web services "bottom-up" (code-first).
When doing this, how can I add restrictions within my XSD <types> WSDL element? For example, in the following:
import javax.jws.WebService;
import javax.xml.ws.BindingType;
#WebService
#BindingType(javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING)
public class ProofOfConcept {
public String sayHello(String guest){
return "Hello "+guest;
}
}
The output in my WSDL is as such:
...
<xs:complexType name="sayHello">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="xs:string" />
</xs:sequence>
</xs:complexType>
...
I would like to add both minLength and maxLength restrictions to the "guest" String. Is this possible to do through code as opposed to editing the WSDL directly?
I'm aware that I can use a custom class and annotate my fields with #XmlElement or #XmlAttribute to get a few customizations (name, required etc.), but nothing specific for something like a String (length etc.); is it possible to modify / extend those annotations to add support for something like length or pattern?
Thanks.
I find it interesting that your policy is to try to build Web services bottom-up as I recommend building Web services top-down in order to cleanly define the contract. At any rate...
I'm not aware of a specific solution using annotations and I see that you haven't found one in your search either. If you want to automate the process so as to avoid overwriting your changes to the schema every time you regenerate the WSDL you can add an XSL transform to the resulting WSDL to add these attributes. While not an ideal solution it should allow you to set the attributes and continue working.

Creating a custom JAXB annotation

Do people have any recommendations on how i could write my own custom JAXB annotation handling class to support the generation of xs:annotation/xs:documentation elements in the xsd schema?. I'd like to create a new java annotation "#XmlAnnotation" which would include a "documentation" attribute. I'd then make these classes available to the JAXB schema generator via the classpath. The schema generator would then take this sample java code
#XmlRootElement(name="ClientData")
public class ClientData {
/**
* The first address field of the person
*/
#XmlAnnotation(documentation="The first address field of the client")
private String address1 = null;
}
and create this xsd schema
<xs:complexType name="clientData">
<xs:sequence>
<xs:element minOccurs="0" name="address1" type="xs:string">
<xs:annotation>
<xs:documentation>The first address field of the client</xs:documentation>
</xs:annotation>
Would it be easier to extend from the existing #XmlElement annotation class, and just add support of an extra documentation attribute?
This XmlRootElement is almost empty ( http://docjar.org/src/api/javax/xml/bind/annotation/XmlRootElement.java, there is not 'active' code inside
The main modification would be to change the code using this annotation and generating the xsd file.
That would only allow the construct for the root element. It is valid in just about ALL the xs namespace elements.
I don't understand why it wasn't supported as standard.

Categories

Resources