A very generalized and simplified scenario description:
Application builds JAXB instances through UI,
The JAXB instances are built in different steps are composed in the end,
As this is done, I must perform validation of the JAXB instances created in each step against the XSD to verify that the UI tier does not build nonsense.
Now the schema defines complex types A, B and C. The schema specifies that XML document must have top level element A and this can contain multiple B and this can optionally include C. I create JAXB instance for B and absolutely want to validate this against the complex type definition in the XSD before nesting it under A. However validation of this will fail if I validate against the entire XSD.
Questions: How to validate JAXB instance against only a part of the XSD from which its class has been generated from?
How about using schemagen to generate schema from the JAXB class which instance I want to validate and then validate against that? Do you think that can work? Any other ideas?
I have no previous experience with schemagen and will start prototyping of this solution soon.
Note: in reality, the schemas are not as simple as in the above example and the solution of creating some always-valid mock of A is not feasible option. Not to mention that this kind of validation will be on hundred places to say the least.
create separate schema for element you want to validate, where this element is on root level.
to workaround missing #xmlRoottag see 101 ways to marshal objects with JAXB
Well it turns out that using xsi:type let's one accomplish this.
http://www.w3.org/TR/xmlschema-1/#xsi_type
While working with xjc and schemagen tools please consider same concept which we use in java. In java, every class is a skeleton and object is an instance. Same as we need to consider XSD as skeleton and XML as instance.
xjc tool:- Xsd to Java Class Or Unmarshaling
Consider the below XSD with namespaces for example. The xjc tool will generate the java class along with package-info and object-factory.
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ir="http://irctc.org/service" targetNamespace="http://irctc.org/service">
<xsd:element name="Customer" type="ir:CustomerType"/>
<xsd:complexType name="CustomerType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="src" type="xsd:string"/>
<xsd:element name="dest" type="xsd:string"/>
<xsd:element name="price" type="xsd:float" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
Use below command
D:\>xjc Customer.xsd
parsing a schema...
compiling a schema...
org\irctc\service\CustomerType.java
org\irctc\service\ObjectFactory.java
org\irctc\service\package-info.java
Note :- If you have multiple java class then you can use jaxb.index file instead of using ObjectFactory.java.
schemagen tool:- Java Class to Xsd Or Marshaling
Let us suppose we want to generate the Xsd file using java class then first we need to create the ObjectFactory.java Or jaxb.index file and package-info.java in the same package where we have the CustomerType.java.
package org.irctc.service;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement("Customer")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "CustomerType", propOrder = {
"name",
"src",
"dest",
"price"
})
public class CustomerType {
#XmlElement(required = true)
protected String name;
#XmlElement(required = true)
protected String src;
#XmlElement(required = true)
protected String dest;
protected Float price;
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
}
public String getSrc() {
return src;
}
public void setSrc(String value) {
this.src = value;
}
public String getDest() {
return dest;
}
public void setDest(String value) {
this.dest = value;
}
public Float getPrice() {
return price;
}
public void setPrice(Float value) {
this.price = value;
}
}
Use below command
D:\>schemagen org.irctc.service.CustomerType
Note: Writing D:\schema1.xsd
The above command will generate the xsd file. The package of java class will considered as xsd namespaces.
The above details are only for understanding marshaling and unmarshaling process using tools in Jax-B API.
For more details , check below examples
Jax-B Hello World Example
Java-XML mapping with Jax-B 2.0
Related
Been grappling with a problem for about half a week now, and need help as I cannot seem to make any headway.
I've inherited an application which hasn't been treated nicely for 8 years....still on Java 1.4, Maven1 build, no new unit tests for 8 years...
Currently the upgrade to Java 1.6 (Java 1.8 branch also done in parallel, will test both) and Maven 3.3.3 is well in swing - have been making excellent headway.
Now I've hit a wall and not made a breakthrough for a while.
The old sources used local JAXB 1.3 jars to generate classes from a large XSD.
I had to migrate from JAXB1.3 to JAXB2.1 - which also meant i had to spend a lot of time rewriting all the references to the generated classes as the naming conventions changed.
Anyway, a lot of time was spent getting the code to compile.
Finally, it compiles, and I try out a unit test to see how it works.
This is where i hit my problem.
Most of the classes compiled work fine, but three of the packages throw exceptions when i try to generate the JAXBContext:
#XmlValue is not allowed on a class that derives another class.
I've narrowed the problem down to a pattern which occurs in a couple of the generated classes.
The class that causes the exception is defined in the schema as below:
<xs:element name="ContactName">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="First" type="xs:string"/>
<xs:attribute name="Middle" type="xs:string"/>
<xs:attribute name="Last" type="xs:string"/>
<xs:attribute name="Name" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
and then this element is referenced in another as follows:
<xs:element name="ContactInfo">
<xs:complexType>
<xs:annotation>
<xs:documentation>Common contact information</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element ref="ContactName" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="ContactID" minOccurs="0"/>
<xs:element ref="ContactDivision" minOccurs="0" maxOccurs="unbounded"/>
.....
this is generated into:
ContactName:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"value"
})
#XmlRootElement(name = "ContactName")
public class ContactName
extends BaseJaxbDoc
implements Serializable, Cloneable, CopyTo
{
private final static long serialVersionUID = 47110815L;
#XmlValue
protected String value;
#XmlAttribute(name = "First")
protected String first;
#XmlAttribute(name = "Middle")
protected String middle;
#XmlAttribute(name = "Last")
protected String last;
#XmlAttribute(name = "Name")
protected String name;
And then declared in ContactInfo as follows:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"contactName",
"contactID",
"contactDivision",
"contactPhone",
"contactPhoneHome",
"contactPhoneMobile",
"contactFax",
"contactEmail",
"contactEmail2"
})
#XmlRootElement(name = "ContactInfo")
public class ContactInfo
extends BaseJaxbDoc
implements Serializable, Cloneable, CopyTo
{
private final static long serialVersionUID = 47110815L;
#XmlElement(name = "ContactName")
protected List<ContactName> contactName;
The exception thrown is at:
this problem is related to the following location:
at protected java.lang.String xxx.xx.xxxx.xxxx.orders.jaxb.ContactName.value
at xxx.xx.xxxx.xxxx.orders.jaxb.ContactName
at protected java.util.List xxx.xx.xxxx.xxxx.orders.jaxb.ContactInfo.contactName
at xxx.xx.xxxx.xxxx.orders.jaxb.ContactInfo
at protected java.util.List xxx.xx.xxxx.xxxx.orders.jaxb.CustomerReference.contactInfo
at xxx.xx.xxxx.xxxx.orders.jaxb.CustomerReference
at protected java.util.List xxx.xx.xxxx.xxxx.orders.jaxb.Item.customerReference
at xxx.xx.xxxx.xxxx.orders.jaxb.Item
at public xxx.xx.xxxx.xxxx.orders.jaxb.Item xxx.xx.xxxx.xxxx.orders.jaxb.ObjectFactory.createItem()
at xxx.xx.xxxx.xxxx.orders.jaxb.ObjectFactory
There is an XML transformation on the original schema, stripping comments out and creating jaxb:typesafeEnum types. Then the transformed schema is used with a jxb binding file to bind everything to an internal jaxb helper superclass - BaseJaxbDoc
<jaxb:globalBindings generateIsSetMethod="true">
<xjc:serializable uid="47110815"/>
<xjc:superClass name="xxx.xx.xxxx.xxxx.helpers.BaseJaxbDoc"/>
<jaxb:javaType name="java.math.BigDecimal" xmlType="xs:decimal"
parseMethod="xxx.xx.xxxx.xxxx.helpers.AmountConverter.parseAmount"
printMethod="xxx.xx.xxxx.xxxx.helpers.AmountConverter.printAmount"/>
</jaxb:globalBindings>
This is because I am using xjc on 9 different schemas, all generating JAXB packages of classes.
The classes all have the same superclass (defined in a bindings file for each schema) to only implement the JAXB marshall/unmarshall classes once, along with some other helper functions.
So my question is how to get around this exception when i cannot modify the schema?
Something in the XSLT or something in the bindings file?
My Maven dependencies:
for JAXB:
org.jvnet.jaxb2.maven2
maven-jaxb21-plugin
0.13.0
JAXB runtime:
org.glassfish.jaxb
jaxb-runtime
2.2.11
Try annotating BaseJaxbDoc with #XmlTransient.
The problem you're getting is produced here:
if(getBaseClass()!=null) {
builder.reportError(new IllegalAnnotationException(
Messages.XMLVALUE_IN_DERIVED_TYPE.format(), p ));
}
JAXB thinks your BaseJaxbDoc is a base class. So you should either remove xjc:superClass or trink JAXB into thinking that your class does not have a base class.
When I look at this part of the code in the ModelBuilder:
if(reader.hasClassAnnotation(clazz,XmlTransient.class) || isReplaced) {
// handle it as if the base class was specified
r = getClassInfo( nav.getSuperClass(clazz), searchForSuperClass,
new ClassLocatable<C>(upstream,clazz,nav) );
}
It seems that the ModelBuilder recognizes #XmlTransient on classes and does not consider them. So there's a chance that assing #XmlTransient on your BaseJaxbDoc would help.
Another option is to drop BaseJaxbDoc construct. You use class inheritance to add marshal/unmarshal functionality to the schema-derived classes. I'd rather move this functionality out into some external services. This is probably not an option here as you're probably facing a lot of legacy code.
A further option is to try MOXy instead of JAXB RI in the runtime.
#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)
I have a JAXB generated class PersonType with the properties name, phone, address. In the XSD I define that "phone" minoccurs="1".
How can I test programatically(i.e. via reflection, etc), in Java code, if the property "phone" is required or not in the XSD?
Later edit:
The JAXB generator creates a class without any "required", etc. attributes. So there's no annotation on the class or it's fields to indicate if it's required or not. However, there's the #XmlElement annotation on the field specifying the XSD equivalent element. So I guess the only solution is to inspect this annotation and then make an XPath or something against the XSD?
Thank you!
The #XmlElement annotation has a required property (false by default) that is used to indicate that an XML element is required (minOccurs > 0). You could use the java.lang.reflect APIs to get this value from the class.
XML Schema
In the XML schema below the foo element is required and the bar element is not.
<?xml version="1.0" encoding="UTF-8"?>
<schema
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org/schema"
xmlns:tns="http://www.example.org/schema"
elementFormDefault="qualified">
<complexType name="root">
<sequence>
<element name="foo" type="string"/>
<element name="bar" type="string" minOccurs="0"/>
</sequence>
</complexType>
</schema>
Generated Class
Below is the class that was generated from the XML schema. We see that the foo field is annotated with #XmlElement(required=true) anf the bar field is not.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "root", propOrder = {
"foo",
"bar"
})
public class Root {
#XmlElement(required = true)
protected String foo;
protected String bar;
}
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
In the reference JAXB implementation is there anyway to get XmlSeeAlso to use the name= value from XmlRootElement?
The effect I want is for the type attribute to use the name= value rather than actual class name from XmlSeeAlso.
Is this possible is some other JAXB implementation?
Small example:
#XmlRootElement(name="some_item")
public class SomeItem{...}
#XmlSeeAlso({SomeItem.class})
public class Resource {...}
XML:
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="some_item">
...
</resource>
Possible without a lot of effort?
About #XmlSeeAlso
The purpose of the #XmlSeeAlso annotation is just to let your JAXB (JSR-222) implementation know that when it is processing the metadata for Resource that it should also process the metadata for the SomeItem class. Some people mistakenly believe that it is related to mapping inheritance since that is the use case it is most often used with. Since the subclasses of a class can not be determined using Java reflection, #XmlSeeAlso is used to let the JAXB implementation know that mappings for the subclasses should also be created.
Below is an example of how you could support your use case:
Resource
The complex type name corresponding to a Java class is supplied via the #XmlType annotation.
package forum12288631;
import javax.xml.bind.annotation.XmlType;
#XmlType(name="some_item")
public class Resource {
}
Demo
The root element name can come from the #XmlRootElement annotation or can be supplied via an instance of JAXBElement. We will create an instance of JAXBElement and indicate that it is holding onto an instance of Object. When marshalled this will for the xsi:type attribute to be included in the output.
package forum12288631;
import javax.xml.bind.*;
import javax.xml.namespace.QName;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Resource.class);
Resource resource = new Resource();
JAXBElement<Object> jaxbElement = new JAXBElement<Object>(QName.valueOf("resource"), Object.class, resource);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(jaxbElement, System.out);
}
}
Output
The resulting XML has the root element supplied by the JAXBElement and the value of the xsi:type attribute comes from the #XmlType annotation on Resource.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="some_item"/>