Missing JAXB classes from XMLschema.xsd - java

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.

Related

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"/>

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

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.

JAXB javaType customization on xs:integer produces #XmlElement with "type=String.class"

When generating Java beans from a XSD with XJC, I need to map xs:integer to Integer rather than BigInteger. I added a javaType tag to my JAXB customization file (as said in many answers from this site), and it worked fine.
But in the generated code I noticed that the #XmlElement tag now has a type=String.class parameter.
So now I wonder, why String?
Is it because the parse and print methods are converting from/to string objects?
I tried with xjc:javaType instead of jaxb:javaType, allowing me to replace the generated Adapter1<String, Integer> with a custom MyAdapter<BigInteger, Integer>, but exactly the same thing happened.
If this is normal XJC behavior, is it possible to tweak it to generate code without this parameter, or with another value than String?
Note that everything is working fine, but I would like to understand.
Also I'm using Enunciate to document my API and it seems to be confused by this type thing (but this is probably a bug in Enunciate).
I'm using JAXB RI 2.2.6, and here are some pieces of code, to illustrate my question:
bindings.xjb
<jaxb:bindings version="2.0" ...>
<jaxb:globalBindings>
<jaxb:javaType
name="java.lang.Integer"
xmlType="xs:integer"
parseMethod="..."
printMethod="..." />
</jaxb:globalBindings>
</jaxb:bindings>
Field definition in the XSD
<xs:complexType name="MyType">
<xs:sequence>
<xs:element name="myField" type="xs:integer" />
</xs:sequence>
</xs:complexType>
Generated Java field
#XmlElement(namespace = "...", required = true, type = String.class)
#XmlJavaTypeAdapter(Adapter1.class)
#XmlSchemaType(name = "integer")
protected Integer myField;
I know this is an old question, but for the people still looking for an answer:
using type xs:int instead of xs:integer will create a normal java int instead of the Biginteger.

How can you handle invalid enum types using JAXB?

We are using JAXB to handle our wsdl validation. In the wsdl we have an enumeration like so:
<xs:simpleType name="myEnum">
<xs:annotation>
<xs:documentation>The enums which matter.</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:enumeration value="MYENUM_1">
<xs:annotation>
<xs:documentation>MYENUM_1 is the first enum.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="MYENUM_2">
<xs:annotation>
<xs:documentation>MYENUM_2 is the second enum.</xs:documentation>
</xs:annotation>
</xs:enumeration>
</xs:restriction>
</xs:simpleType>
If we pass in an invalid string as an enum for example, MYENUM_7, the value is set to null, instead of throwing an error like we would expect. Digging into the code, we find the following from RuntimeEnumLeafInfoImpl.java:
package com.sun.xml.bind.v2.model.impl;
//...
public T parse(CharSequence lexical) throws AccessorException, SAXException {
// TODO: error handling
B b = baseXducer.parse(lexical);
if(b==null) {
return null;
}
return parseMap.get(b);
}
It is clear to see that the parseMap is our list of enums, but if the key to the map, in this case the value of b, is not in the map it just returns null. We would like it to throw and exception of some sort if b is not in parseMap.
Short of fixing this and recompiling our own JAXB is there some other way to solve this?
EDIT: for clarification
We are using JAXB 2.1.9, and we want to unmarshall the data AND validate it. It is certainly possible I have overlooked something in the documentation about validation.
You are saying that you are using JAXB to validate data, but you are only describing an unexpected result after unmarshalling an XML document. In JAXB, validation and unmarshalling are two different and perhaps independent issues, since you can use JAXB in at least three different modes:
don't validate
validate, report errors, but continue unmarshalling
validate and break on errors
Are you sure that you've enabled schema validation on your unmarshaller and that you are evaluating potential errors? How to do that is rather different, depending on if you're using JAXB 1.0 or 2.0, but the documentation should tell you how.

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