Creating a custom JAXB annotation - java

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.

Related

JAXB translate element name when marshalling/un-marshalling

I am generating a java class from xsd which is used in to marshal/un-marshal xml.
I have an element currently defined in my xsd as
<xs:element maxOccurs="1" minOccurs="0" name="versionLabel" type="xs:string"/>
which results in a java class containing
String versionLabel
and setters and getters, setVersionLabel()/getVersionLabel().
I want the incoming/outgoing xml element to be <version> and for that to translate to/from the java class property "versionLabel". How do I do define that behavior in the xsd?
add #XmlElement annotation to the attribute and update your xsd if it used for any validation
#XmlElement(name = "version")
String versionLabel;

Parsing GPX XML "any"-Element from XSD generated classes [duplicate]

I have an application doing XML<->conversions using Jaxb and automatically generated classes with maven-jaxb2-plugin.
Someplace deep in my schema, I have the possibility to enter "ANY" xml.
Update: this better describes my schema. Some known XML wrapping a totally unknown part (the "any" part).
<xs:complexType name="MessageType">
<xs:sequence>
<xs:element name="XmlAnyPayload" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:any namespace="##any"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="OtherElements">
....
</xs:sequence>
This maps (by jaxb) to a inner class like this.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"any"
})
public static class XmlAnyPayload {
#XmlAnyElement(lax = true)
protected Object any;
When I unmarshall the entire structure, it is no problem. The "Object any" will render into a org.apache.xerces.dom.ElementNSImpl. Now, I want to recreate the Java object manually and then go to XML. How do I take some random XML and put into the any (org.apache.xerces.dom.ElementNSImpl) element to be able to build up the Java object?
Also, the next case is when I have this element as java, I want to unmarshall this very part (to be able to extract the XML string of this element). But this is not possible. I get an exception about root elements. But it is not possible to annotate ElementNSImpl.
unable to marshal type "com.sun.org.apache.xerces.internal.dom.ElementNSImpl" as an element because it is missing an #XmlRootElement annotation
Do you have any suggestions on how to handle these problems?
#XmlAnyElement(lax = true) means in plain English something like:
Dear JAXB! If you have a mapping for this element, please unmarshal it
into a Java object. If you don't know this element, just leave it as a
DOM element.
This is exactly what is happening in your case. So if you want to actually unmarshal the content of this lax any, provide JAXB context with a mapping for the element you wish to unmarshal. The easiest way to do this is to annotate your class with #XmlRootElement
#XmlRootElement(name="foo", namespace="urn:bar")
public class MyClass { ... }
Now when you create your JAXB context, add MyClass into it:
JAXBContext context = JAXBContext.newInstance(A.class, B.class, ..., MyClass.class);
In this case, if JAXB meets the {urn:bar}foo element in the place of that xs:any, it will know that this element is mapped onto MyClass and will try to unmarshal MyClass.
If you are creating JAXB context based on the package name (you probably do), you can still add you class (say, com.acme.foo.MyClass) to it. The easiest way is to create a com/acme/foo/jaxb.index resource:
com.acme.foo.MyClass
And the add your package name to the context path:
JAXBContext context = JAXBContext.newInstance("org.dar.gee.schema:com.acme.foo");
There are other ways with ObjectFactory etc., but the trick with jaxb.index is probably the easiest one.
Alternatively, instead of unmarshalling everything in one run, you can leave the content of xs:any as DOM and unmarshal it into the target object in a second unmarshalling with anothe JAXB context (which know your MyClass class). Something like:
JAXBContext payloadContext = JAXBContext.newInstance(MyClass.class);
payloadContext.createUnmarshaller().unmarshal((Node) myPayload.getAny());
This approach is sometimes better, especially when you have a combination of container/payload schemas which are relatively independent. Depends on the case.
All said above applies to marshalling as well. It's all neatly bidirectional.
I think you need the XSDs for this "any" part and generate classes for them as well.
Here is some more information:
http://jaxb.java.net/guide/Mapping_of__xs_any___.html
Edit: if your object you want to marshal doesn't have the #XmlRootElement annotation (see error message), then I think you have to wrap it with a JAXBElement.
<xs:any/>
requires some not intuitive stuff to be converted to java object. If you have no difference, try using
<element name="any" type="xs:anyType"/>

How to get the minOccurs / maxOccurs values from the XSD in Java using JAXB?

My application is calling a webservice and I have generated the Java classes from the WSDL/XSDs with the maven-jaxb2-plugin. The webservice calls worked fine for a while but recently I had a problem on marshalling an object into XML:
[org.xml.sax.SAXParseException: cvc-complex-type.2.4.d: Invalid content was found starting with element 'ns1:TheFooAndBarThing'.
No child element '{"http://www.myschemanamespace.xyz/v1":BarId}' is expected at this point.]
The XSD part looks like this:
<xs:complexType name="TheFooAndBarThing">
<xs:sequence>
<xs:element name="FooId" minOccurs="1" maxOccurs="1" type="nx:FooIdType"/>
<xs:element name="BarId" minOccurs="1" maxOccurs="100" type="nx:BarIdType"/>
</xs:sequence>
</xs:complexType>
The generated class TheFooAndBarThing looks like this (Javadoc removed):
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "TheFooAndBarThing", propOrder = {
"fooId",
"barId"
})
public class TheFooAndBarThing {
#XmlElement(name = "FooId", required = true)
protected String fooId;
#XmlElement(name = "BarId", required = true)
protected List<String> barId;
public String getFooId() {
return fooId;
}
public void setFooId(String value) {
this.fooId = value;
}
public List<String> getBarId() {
if (barId == null) {
barId = new ArrayList<String>();
}
return this.barId;
}
}
It cost me some time and coffee to find out the real problem. My mistake was that I put more than 100 BarId elements in my list.
So here's my question:
How can I get the maxOccurs/minOccurs value from the XSD into my Java code so that I can use it as a max/min value while building my list of elements?
Short answer: there is no easy way.
Schema-derived classes have no reference to the original schema anymore. Even if you use something as XSOM or whatever to parse the original schema, you will not be able to find corresponding XML Schema constructs to check.
The best way to address the issue would be writing a custom XJC plugin (I wrote quite a few of them).
When XJC compiles the schmema it first creates a model, then a so-called outline (pre-rendered code) and then renders the code. The model still has information about the original XML Schema constructs, so you can find all the relevant min/maxOccurs information there.
The problem is just that you don't always have 1:1 mapping between schema constructs and properties of schema-derived classes. Sometimes several elements are mapped onto one property. There's a huge number of exceptions and special cases. You can get it working for the straightforward cases, though. Anyway, the task is not easy.
Use could try JAXB-Facets.
Here is an example of setting specific values for minoccurs and maxoccurs:
#MinOccurs(value = 0)
#MaxOccurs(value = 100)
private List<String> test;

Missing JAXB classes from XMLschema.xsd

I'm working on a xml schema resolver and I'm using JAXB with XMLSchema.xsd.
I experience problems with JAXB, because I don't get classes for all the top level elements. For example for
<xs:element name="maxLength" id="maxLength" type="xs:numFacet">
I do not get a class MaxLength or anything like that. Only NumFacet exists.
Anyone else experienced that and could please help me?
Cheers,
XLR
As far as I remember jaxb, the schema compiler xjc creates classes for each complex type of the schema given. Thus, if you like to have a class MaxLength you should add a complex type declaration to your schema:
<xs:complexType name="MaxLength">
<xs:attribute name="value" type="xs:int"/>
</xs:complexType>
<xs:element name="MyMaxLength" type="MaxLength"/>
You should now get a class MaxLength with a member variable value of type integer.
JAXB will not generate a class for anything that already has a type, and neither do you need one.
If you unmarshal a global element like your maxLength element, then JAXB will return you a JAXBElement wrapping the NumFacet type. Something like this:
JAXBElement<?> root = unmarshaller.unmarshal(myStream);
NumFacet value = (NumFacet) root.getValue();
There are other methods on JAXBElement to find out what the element name was, etc.

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.

Categories

Resources