How to set the namespace of marshalled xml using camel jaxb? - jaxb

For starters, I'm creating some routes using Camel ver 2.15 (in Fuse 6.2.1).
In my route, i'm trying to create a XML from a pojo that was generated using cxf-xjc maven plugin (cxf-xjc read some xsd somewhere then from the xsd, the pojos with jaxb annotations were produced).
The pojos are TempProject and TempProjects.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(
name = "",
propOrder = {"ecode", "tempName"}
)
#XmlRootElement(
name = "TempProject"
)
public class TempProject implements Serializable {
#XmlElement(
name = "Ecode",
required = true
)
protected String ecode;
#XmlElement(
name = "TempName",
required = true
)
protected String tempName;
public TempProject() {
}
public String getEcode() {
return this.ecode;
}
public void setEcode(String value) {
this.ecode = value;
}
public String getTempName() {
return this.tempName;
}
public void setTempName(String value) {
this.tempName = value;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(
name = "",
propOrder = {"tempProjects"}
)
#XmlRootElement(
name = "TempProjects"
)
public class TempProjects implements Serializable {
#XmlElement(
name = "TempProject",
required = true
)
protected List<TempProject> tempProjects;
public TempProjects() {
}
public List<TempProject> getTempProjects() {
if (this.tempProjects == null) {
this.tempProjects = new ArrayList();
}
return this.tempProjects;
}
}
I can generate the xml using this code:
JAXBContext jaxbContext = JAXBContext.newInstance(new Class[]{TempProjects.class});
jaxbContext.createMarshaller();
JaxbDataFormat jaxbDataFormat = new JaxbDataFormat(jaxbContext); //import org.apache.camel.converter.jaxb.JaxbDataFormat;
I call
.marshal(jaxbDataFormat)
in my route to effect the marshalling from the pojo to xml.
The generated xml is posted below:
<TempProjects xmlns="http://blah.blah/foo/schema/v2">
<TempProject>
<Ecode>1</Ecode>
<TempName>Tempname1</TempName>
</TempProject>
<TempProject>
<Ecode>2</Ecode>
<TempName>Tempname2</TempName>
</TempProject>
How can i generate a marshalled xml that will have a namespace like this...
<TempProjects xmlns:myprefix="http://blah.blah/foo/schema/v2">
Reason being why I needed a namespaceprefix is because I plan to split the values (e.g. Ecode) in the xml using xpath and I needed a namespaceprefix to do that (thats what ive researched, i might be wrong).
My planned code in my route is
.marshal(jaxbDataFormat)
.split( xpath("/TempProjects/TempProject/Ecode/text()").namespaces(ns1),
new ProjectIdsAggregator()) //the above xpath doesn't work because it doesn't have a namespace prefix
//Namespaces ns1 = new Namespaces("myprefix", "http://blah.blah/foo/schema/v2" );
I looked at jaxbDataFormat.setNamespacePrefixRef("myprefix"), but i got an error (org.apache.camel.NoSuchBeanException: No bean could be found in the registry for: myprefix of type: java.util.Map)
I'm actually quite new in the apache camel routing world, so i might be missing some basic stuff.

You don't need to change your XML at all. It is fine.
With the XML you posted and the Namespace declaration you posted, the following XPath works fine to split the XML (as an example) into two TempProject parts:
xpath("/myprefix:TempProjects/myprefix:TempProject").namespaces(ns1)
Because you declared the XML namespace like this:
Namespaces ns1 = new Namespaces("myprefix", "http://blah.blah/foo/schema/v2" )
Your XPath must use the prefix myprefix for all elements:
/myprefix:TempProjects/myprefix:TempProject

Related

MapStruct unable to generate mapper for XJC (JAXB) generated classes

I'm struggling since a couple of hours trying to get MapStruct generate a valid mapper for JAXB generated classes. The particularity of these classes is that they don't have neither setters nor adders for collections. For example:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "IndividualType", propOrder = {"addressTypes","pensionTypes"})
public class IndividualType
{
...
#XmlElement(name = "addressType")
protected List<AddressType> addressTypes;
#XmlAttribute(name = "firstName", required = true)
protected String firstName;
...
public List<AddressType> getAddressTypes()
{
if (addressTypes == null) {
addressTypes = new ArrayList<AddressType>();
}
return this.addressTypes;
}
public String getFirstName()
{
return firstName;
}
public void setFirstName(String value)
{
this.firstName = value;
}
...
}
The class avove have a getter and a setter for attributes (firstName in this example) but for collections (List here) it only has a getter. Hence it's the consumer responsibility to access via getAddressTypes(add (new AddressType(...)).
The MapStruct mapper for such a class is as follows:
#Mapper(collectionMappingStrategy = CollectionMappingStrategy.TARGET_IMMUTABLE, uses = {AddressTypeMapper.class}, unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = "spring")
public interface IndividualTypeMapper
{
IndividualType toIndividualType(IndividualEntity individual);
#InheritInverseConfiguration
IndividualEntity fromIndividualType(IndividualType individualType);
}
And the MapStruct generated code is:
#Override
public IndividualEntity fromIndividualType(IndividualType individualType)
{
if ( individualType == null )
return null;
IndividualEntity individualEntity = new IndividualEntity();
individualEntity.setFirstName( individualType.getFirstName() );
...
return individualEntity;
}
In the generated code above, only the properties having a setter get initialized despite the usage of the TARGET_IMMUTABLE strategy.
Any suggestions please ? Of course, a simple constructor would perfectly do but, for some reason, people seems to prefer complicated and nonworking solutions to simple working ones and, consequently, I have to use MapStruct :-(
Many thanks in advance.
Marie-France
The reason why it is not working is due to the fact that you are using CollectionMappingStrategy.TARGET_IMMUTABLE. With that you are basically telling MapStruct my collection targets are immutable and will throw an exception if you try to modify the collection returned by the getter.
I would suggest removing the collectionMappingStrategy and see whether it works without it.

How to solve the JAXB IllegalAnnotationException error

I am trying to consume a web service using JAX-WS, and it throws me the following error corresponding to the following classes:
Class 1
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "validarComprobante", propOrder = { "xml" })
public class ValidarComprobante {
protected byte[] xml;
public byte[] getXml() {
return xml;
}
public void setXml(byte[] value) {
this.xml = value;
}
}
Class 2
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "validarComprobanteResponse", propOrder = { "respuestaRecepcionComprobante" })
public class ValidarComprobanteResponse {
#XmlElement(name = "RespuestaRecepcionComprobante")
protected RespuestaSolicitud respuestaRecepcionComprobante;
public RespuestaSolicitud getRespuestaRecepcionComprobante() {
return respuestaRecepcionComprobante;
}
public void setRespuestaRecepcionComprobante(RespuestaSolicitud value) {
this.respuestaRecepcionComprobante = value;
}
}
These classes were generated with wsimport.exe which is inside the Java 8 SDK. The error is as follows:
com.sun.xml.ws.spi.db.DatabindingException: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
Two classes have the same XML type name "{http://ec.gob.sri.ws.recepcion}validarComprobante". Use #XmlType.name and #XmlType.namespace to assign different names to them.
this problem is related to the following location:
at com.horus.microservices.ebillinginvoice.sri.recepcion.ValidarComprobante
at public javax.xml.bind.JAXBElement com.horus.microservices.ebillinginvoice.sri.recepcion.ObjectFactory.createValidarComprobante(com.horus.microservices.ebillinginvoice.sri.recepcion.ValidarComprobante)
at com.horus.microservices.ebillinginvoice.sri.recepcion.ObjectFactory
this problem is related to the following location:
at recepcion.ws.sri.gob.ec.ValidarComprobante
Two classes have the same XML type name "{http://ec.gob.sri.ws.recepcion}validarComprobanteResponse". Use #XmlType.name and #XmlType.namespace to assign different names to them.
this problem is related to the following location:
at com.horus.microservices.ebillinginvoice.sri.recepcion.ValidarComprobanteResp
at public com.horus.microservices.ebillinginvoice.sri.recepcion.ValidarComprobanteResp com.horus.microservices.ebillinginvoice.sri.recepcion.ObjectFactory.createValidarComprobanteResponse()
at com.horus.microservices.ebillinginvoice.sri.recepcion.ObjectFactory
this problem is related to the following location:
at recepcion.ws.sri.gob.ec.ValidarComprobanteResponse
at com.sun.xml.ws.db.glassfish.JAXBRIContextFactory.newContext(JAXBRIContextFactory.java:105)
at
Please any help
Thanks

MOXy creates different namespace prefix than JAXB RI

I am trying to switch from JAXB RI 2.2.7 to MOXy JAXB 2.5.2 implementation.
I notice the following difference in namespace and prefix in the generated XML output snippet:
JAXB RI:
<xbrli:xbrl xmlns:xbrli="http://www.xbrl.org/2003/instance" xmlns:bd-algemeen="http://www.nltaxonomie.nl/7.0/basis/bd/items/bd-algemeen" xmlns:iso4217="http://www.xbrl.org/2003/iso4217">
<bd-algemeen:OperatingSystem contextRef="cc_131">W</bd-algemeen:OperatingSystem>
<xbrli:unit id="uu_692">
<xbrli:measure>iso4217:EUR</xbrli:measure>
</xbrli:unit>
</xbrli:xbrl>
MOXy:
<xbrli:xbrl xmlns:xbrli="http://www.xbrl.org/2003/instance" xmlns:bd-algemeen="http://www.nltaxonomie.nl/7.0/basis/bd/items/bd-algemeen">
<bd-algemeen:OperatingSystem contextRef="cc_131">W</bd-algemeen:OperatingSystem>
<xbrli:unit id="uu_662">
<xbrli:measure xmlns:ns0="http://www.xbrl.org/2003/iso4217">ns0:EUR</xbrli:measure>
</xbrli:unit>
</xbrli:xbrl>
1) How do I configure MOXy to output the same as the RI? The MOXy output is valid but The XML (actually XBRL) document I create must have predefined prefixes.
2) I use a an instance of "com.sun.xml.bind.marshaller.NamespacePrefixMapper" to indicate the prefix that should be used. However MOXy doesn't call this in case of the iso4217 namespace, why (I debugged the MOXy namespace resolver but got a bit lost ;)?
BTW: the prefixMapper is used by Moxy for other namespaces (like the "xbrli"), but not for this iso4217, what is the difference?
I haved added "bd-algemeen", as the prefix mapper is called for the prefix "bd-algemeen". This mapping doesn't use QName as used by iso4217.
3) I tried the #XmlSchema annotation in the pacakage-info.java in the generated iso4217 pacakge, to define the prefix, but no luck, neither do I want to use this as it concerns generated code that is easily overwritten.
The domain object:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "divide", "measures" })
#XmlRootElement(name = "unit")
public class Unit {
protected Divide divide;
#XmlElement(name = "measure")
protected List<QName> measures;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "operatingSystem"})
public static class TaxDataBedrijf {
#XmlElement(name = "OperatingSystem", namespace = "http://www.nltaxonomie.nl/7.0/basis/bd/items/bd-algemeen")
protected Astring1ItemType operatingSystem;
The creation of the Unit instance:
final Unit item = new Unit();
item.getMeasures().add(new QName("http://www.xbrl.org/2003/iso4217", "EUR", "iso4217"));
taxData = new TaxDataBedrijf();
taxData.setOperatingSystem(createOperatingSystem(context, 'W'));
I thought it might be because of the QName usage, but this is also directly used in other places and is used correctly. For example, Moxy is able to generate this snippet:
<xbrli:xbrl xmlns:xbrli="http://www.xbrl.org/2003/instance" xmlns:bd-domains="http://www.nltaxonomie.nl/7.0/basis/bd/domains/bd-domains" xmlns:bd-axes="http://www.nltaxonomie.nl/7.0/domein/bd/axes/bd-axes">
<xbrli:scenario>
<xbrldi:explicitMember dimension="bd-axes:TimeDimension">bd-domains:Current</xbrldi:explicitMember>
</xbrli:scenario>
</xbrli:xbrl>
And the corresponding java binding:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "value" })
#XmlRootElement(name = "explicitMember")
public class ExplicitMember {
#XmlValue
protected QName value;
#XmlAttribute(name = "dimension")
protected QName dimension;
}
And it's creation:
final ExplicitMember item = new ExplicitMember();
item.setDimension(new QName("http://www.nltaxonomie.nl/7.0/domein/bd/axes/bd-axes", "TimeDimension"));
item.setValue(new QName("http://www.nltaxonomie.nl/7.0/basis/bd/domains/bd-domains", "Current"));
Please some advice on how to solve this such that I can use MOXy instead of the RI (where to look in the Moxy code maybe?).
The behaviour you are seeing is a bug, I have opened up the following issue for it:
https://bugs.eclipse.org/452713
I got it working with the correct namespace notation, that is, that it generates:
<xbrli:unit id="uu_382">
<xbrli:measure>iso4217:EUR</xbrli:measure>
</xbrli:unit>
I spend some (lot ;) time debugging the Moxy code and comparing the namespaces that were correctly used and discovered that the ObjectFactory for the above snippet doesn't contain any #XmlElementDecl annotation like the ones that were working. When adding this, it works, namely:
private static final QName EUR_QNAME = new QName("http://www.xbrl.org/2003/iso4217", "EUR", "iso4217");
#XmlElementDecl(namespace = "http://www.xbrl.org/2003/iso4217", name = "EUR", substitutionHeadNamespace = "http://www.xbrl.org/2003/instance", substitutionHeadName = "item")
public JAXBElement<EUR> createEURO() {
return new JAXBElement<EUR>(EUR_QNAME, EUR.class, null, createEUR());
}
I noticed that when the Xbrl (root tag) mapping is created, it will loop through the properties, and one of these properties is the property "itemsAndTuplesAndContexts". It then loop through the associated reference elements (Moxy code: MappingGenerator.generateMappingForReferenceProperty(..)) and will add them to it's namespace resolver map with generated prefix, that is stored in it's descriptor.
During marshalling, it will use the prefix mapper to map it's stored namespaces. Because I added #XmlElementDecl declaration for the iso4217 element, it's found a referencing Element, and as such it's added to the namepace resolver map of the Xbrl root element, and as such used during marshalling in the prefix mapper.
A few questions I don't have very clear:
Why is this #XmlElementDecl required in Moxy, and not by the JaxB RI?
Why wasn't this #XmlElementDecl elements generated during the code generation with XJC?
#Blaise: do you think it's a bug ?
property "itemsAndTuplesAndContexts" declaration:
#XmlElementRefs({ #XmlElementRef(name = "unit", namespace = "http://www.xbrl.org/2003/instance", type = Unit.class),
#XmlElementRef(name = "context", namespace = "http://www.xbrl.org/2003/instance", type = Context.class),
#XmlElementRef(name = "item", namespace = "http://www.xbrl.org/2003/instance", type = JAXBElement.class),
#XmlElementRef(name = "tuple", namespace = "http://www.xbrl.org/2003/instance", type = JAXBElement.class),
#XmlElementRef(name = "footnoteLink", namespace = "http://www.xbrl.org/2003/linkbase", type = JAXBElement.class) })
protected List<Object> itemsAndTuplesAndContexts;

How flexible is JAXB?

I am considering using JAXB for XML parsing but I'm having a couple of issues so far that lead me to believe that it might not be flexible enough for what I want.
I'll be parsing XML that is provided by third parties to conform to an XSD that I'll publish. So I want to be flexible enough to handle files that don't have namespaces or specify an old version of the namespace and may in fact contain invalid elements.
Is this sort of flexibility possible with JAXB? At the moment it fails to parse if the namespace is not provided.
How flexible is JAXB?
Very
So I want to be flexible enough to handle files that don't have
namespaces or specify an old version of the namespace and may in fact
contain invalid elements.
NamespaceFilter
Below is a SAX XmlFilter that can be used to apply a missing namespace.
import org.xml.sax.*;
import org.xml.sax.helpers.XMLFilterImpl;
public class NamespaceFilter extends XMLFilterImpl {
private static final String NAMESPACE = "http://www.example.com/customer";
#Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
super.endElement(NAMESPACE, localName, qName);
}
#Override
public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
super.startElement(NAMESPACE, localName, qName, atts);
}
}
Demo
Below is an example of how you can apply the SAX XMLFilter with JAXB.
import javax.xml.bind.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
public class Demo {
public static void main(String[] args) throws Exception {
// Create the JAXBContext
JAXBContext jc = JAXBContext.newInstance(Customer.class);
// Create the XMLFilter
XMLFilter filter = new NamespaceFilter();
// Set the parent XMLReader on the XMLFilter
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
filter.setParent(xr);
// Set UnmarshallerHandler as ContentHandler on XMLFilter
Unmarshaller unmarshaller = jc.createUnmarshaller();
UnmarshallerHandler unmarshallerHandler = unmarshaller
.getUnmarshallerHandler();
filter.setContentHandler(unmarshallerHandler);
// Parse the XML
InputSource xml = new InputSource("src/blog/namespace/sax/input.xml");
filter.parse(xml);
Customer customer = (Customer) unmarshallerHandler.getResult();
}
}
For More Information
http://blog.bdoughan.com/2012/11/applying-namespace-during-jaxb-unmarshal.html

Jersey JAXB Marshalling sets default namespace to ns4 even though package-info.java is defined otherwise

I have an Jersey API that returns Odata standard responses and consumes the same. There are specific name spaces necessary for these responses. I have a package-info.java class:
#XmlSchema(
xmlns = {
#XmlNs(namespaceURI = "http://www.w3.org/2005/Atom", prefix = ""),
#XmlNs(namespaceURI = "http://schemas.microsoft.com/ado/2007/08/dataservices", prefix = "d"),
#XmlNs(namespaceURI = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata", prefix = "m")
},
namespace = "http://www.w3.org/2005/Atom",
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED,
attributeFormDefault = javax.xml.bind.annotation.XmlNsForm.UNQUALIFIED)
package my.package;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlSchema;
I use JAXB to automatically marshal and unmarshal the incoming request body and outgoing response for me. I have beans annotated for these purposes. Here is an example of one:
#XmlRootElement(name = "entry")
public class Entry
{
private String id;
private String title;
private Date updated;
private AtomLink link;
private Content content;
public Entry()
{
}
public Entry(final Content content)
{
this.content = content;
}
public Entry(final String id, final String title, final Date updated, final AtomLink link, final Content content)
{
this.id = id;
this.title = title;
this.updated = updated;
this.link = link;
this.content = content;
}
#XmlElement(name = "title")
public String getTitle()
{
return title;
}
public void setTitle(final String title)
{
this.title = title;
}
#XmlElement(name = "link")
public AtomLink getLink()
{
return link;
}
public void setLink(final AtomLink link)
{
this.link = link;
}
#XmlElement(name = "id")
public String getId()
{
return id;
}
public void setId(final String id)
{
this.id = id;
}
#XmlElement(name = "updated")
public Date getUpdated()
{
return updated;
}
public void setUpdated(final Date updated)
{
this.updated = updated;
}
#XmlElement(name = "content")
public Content getContent()
{
return content;
}
public void setContent(final Content content)
{
this.content = content;
}
}
The response comes across like this.
<ns4:entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns:ns4="http://www.w3.org/2005/Atom">
<ns4:id>TEwxaTFL</ns4:id>
<ns4:title>my resource</ns4:title>
<ns4:link href="http://127.0.0.1:8080/API/resource(TEwxaTFL)" rel="self"/>
<ns4:content type="application/xml">
<m:properties>
<d:name>temp_170_ruleset</d:name>
<d:shared>false</d:shared>
<d:autorun>false</d:autorun>
</m:properties>
</ns4:content>
</ns4:entry>
As you can see the other namespaces come across just fine. The default name space is coming back with a ns4 prefix rather than no prefix. I need it to be like this:
<entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns="http://www.w3.org/2005/Atom">
<id>TEwxaTFL</id>
<title>my resource</title>
<link href="http://127.0.0.1:8080/API/resource(TEwxaTFL)" rel="self"/>
<content type="application/xml">
<m:properties>
<d:name>temp_170_ruleset</d:name>
<d:shared>false</d:shared>
<d:autorun>false</d:autorun>
</m:properties>
</content>
</entry>
I've tried altering the package-info.java class to remove the name spaces:
removing
#XmlNs(namespaceURI = "http://www.w3.org/2005/Atom", prefix = ""),
and
namespace = "http://www.w3.org/2005/Atom",
and removing one at a time. Doing these things never fix the namespace prefix but affected how posts happened - mapping was not possible.
Can anyone see what I'm missing here? I really don't want to "manually" marshal every response. So I want to avoid a NamespacePrefixMapper solution, unless I can define that without manually marshaling the response. I've read where this is suppose to work.
I use Jersey 1.12, JAXB 2.2
--Outcome--
Using Moxy works. I was struggling with getting it to work because the imports used were still the JaxB that was not working for me. Syntactically using Moxy is the same so there was no overhead of updating code for us. We simply needed to add the jaxb.properties file and update our imports. The only other way we got rid of the default namespaces being like this (ns1, ns4, etc.) was to use XSL on the way out - and that sucks.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
package-info
Using the following package-info with MOXy as the JAXB provider I was able to produce the XML that you are looking for. The line that is commented out is necessary until we finish the fix for the following bug: http://bugs.eclipse.org/365457
#XmlSchema(
xmlns = {
//#XmlNs(namespaceURI = "http://www.w3.org/2005/Atom", prefix = ""),
#XmlNs(namespaceURI = "http://schemas.microsoft.com/ado/2007/08/dataservices", prefix = "d"),
#XmlNs(namespaceURI = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata", prefix = "m")
},
namespace = "http://www.w3.org/2005/Atom",
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED,
attributeFormDefault = javax.xml.bind.annotation.XmlNsForm.UNQUALIFIED)
package forum9795350;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlSchema;
For More Information
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
http://blog.bdoughan.com/2011/11/jaxb-and-namespace-prefixes.html

Resources