MOXy creates different namespace prefix than JAXB RI - jaxb

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;

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 set the namespace of marshalled xml using camel 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

JAXB unmarshalling doesn't work when field is declared as List but doesn't work when same field is declared as ArrayList

I recently started working on a code which contains some JAXB serializable/deserializable classes. One of the classes has a few lists in it and I wanted to add a new list to it. The lists were initially declared as ArrayList. Similarly, the get methods were also returning ArrayList. I changed all of them to List and added the new list as well as List. But after that, I was not able to unmarshal the xml for this object into a JAVA object. When I change the fields back to ArrayList without any other change, the unmarshalling works fine. I have also tried to attach the DefaultValidationEventHandler to the Unmarshaller but it doesn't spit out any error while unmarshalling. Below is how the class looks like with class and variable names change
#XmlRootElement(name = "commandSet")
public class CommandSet {
private final ArrayList<Command> xCommands;
private final ArrayList<Command> yCommands;
#Override
#XmlElementWrapper(name = "xCommands")
#XmlElement(name = "xCommand", type = Command.class)
public ArrayList<Command> getXCommands() {
return this.xCommands;
}
#Override
#XmlElementWrapper(name = "yCommands")
#XmlElement(name = "yCommand", type = Command.class)
public ArrayList<Command> getYCommands() {
return this.yCommands;
}
}
When xCommands and yCommands are declared as List and the getters also return List, the unmarhsalling doesn't work.
In all the examples that I have found for unmarshalling lists, people have used List<> instead of ArrayList<>. Any ideas why is it not working for me with List<>?
Things I Noticed About Your Code
There are a couple of weird things I notice about your code that may or may not factor into your problem. At the very least I suspect the model you are experiencing the problem with is different from the model you posted in your question.
You have the fields marked final, but never initialize them.
You annotated both of the get methods with #Override but since CommandSet doesn't inherit from anything (other than Object), you aren't overriding anything.
Complete Working Example
Java Model
CommandSet
In this version of the CommandSet class I have made one of the properties type ArrayList and the other of type List to demonstrate that they both work. I have also removed the weird things about your code mentioned above.
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement
public class CommandSet {
private final ArrayList<Command> xCommands;
private final List<Command> yCommands;
public CommandSet() {
xCommands = new ArrayList<Command>();
yCommands = new ArrayList<Command>();
}
#XmlElementWrapper(name = "xCommands")
#XmlElement(name = "xCommand")
public ArrayList<Command> getXCommands() {
return this.xCommands;
}
#XmlElementWrapper(name = "yCommands")
#XmlElement(name = "yCommand")
public List<Command> getYCommands() {
return this.yCommands;
}
}
Command
public class Command {
}
Demo Code
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(CommandSet.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("input.xml");
CommandSet commandSet = (CommandSet) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(commandSet, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<commandSet>
<xCommands>
<xCommand/>
<xCommand/>
</xCommands>
<yCommands>
<yCommand/>
<yCommand/>
</yCommands>
</commandSet>

Text nodes are not unmarshalled for combination #XmlMixed + #XmlElementRefs + #XmlElementWrapper

I have the following class:
#XmlRootElement(name = "any-text")
public class AnyText {
#XmlElementWrapper(name = "fixed-text", required = true)
#XmlElementRefs({
#XmlElementRef(name = "year", namespace = "http://foo.org/", type = JAXBElement.class, required = false),
#XmlElementRef(name = "title", namespace = "http://foo.org/", type = JAXBElement.class, required = false)
})
#XmlMixed
protected List<Serializable> fixedText = new ArrayList<Serializable>();
...
which is supposed to represent the following XML:
<any-text xmlns="http://foo.org/">
<fixed-text>The story <title>Alice in Wonderland</title> was printed in <year>1865</year>.</fixed-text>
</any-text>
However when I unmarshall and marshall this XML, I get this:
<any-text xmlns="http://foo.org/">
<fixed-text>
<title>Alice in Wonderland</title>
<year>1865</year>
</fixed-text>
</any-text>
basically all text nodes are gone. I checked the memory model and found out that they are not unmarshalled. When I try to add text nodes programatically and marshall the model, text nodes still do not appear. Any workaround is appreciated.
I am using JAXB v2.2.4, but I tested also v2.2.8 with no success.
This issue is very similar (perhaps identical) to one described here and I have also reported it as JAXB-1107.
This appears to be a bug in the JAXB implementation.
The work around is to split your JAXB entity class into two.
#XmlRootElement(name = "any-text")
public class AnyText {
#XmlElement(name = "fixed-text", required = true)
protected FixedText fixedText;
...
}
public class FixedText {
#XmlElementRefs({
#XmlElementRef(
name = "year",
namespace = "http://foo.org/",
type = JAXBElement.class,
required = false),
#XmlElementRef(
name = "title",
namespace = "http://foo.org/",
type = JAXBElement.class,
required = false)
})
#XmlMixed
protected List<Serializable> fixedText = new ArrayList<Serializable>();
}

How does MOXy handle empty arrays

In my Jersey project, I am using MOXy to marshal to/from JSON. One of the classes I want to to marshal is an array of strings that could be empty.
class Data
{
#XmlElement(nillable = true) public String[] array;
}
In the case where the array is empty, I would like the output to be:
{
"array" : []
}
however it looks like MOXy is filtering out empty arrays from the output. How can I get it to include empty arrays in the output?
I was thinking about adding JSONConfiguration.mapped().array("array").build() to the MOXy provider constructor, but this doesn't seem to make a difference (and I'm not even sure that it's the correct solution).
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
ORIGINAL ANSWER
The following enhancement request has been entered. You can use the link below to track our progress on this issue.
http://bugs.eclipse.org/378894
UPDATE
Starting with the May 19, 2012 EclipseLink 2.4.0 label you can set the following property to get the behaviour you are looking for.
marshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, true);
You can download nightly EclipseLink labels from the following location:
http://www.eclipse.org/eclipselink/downloads/nightly.php
Root
On the following class we have three List properties. Two are of the List objects are empty and one is null. Note that the emptyChoiceList field is mapped with #XmlElements. The #XmlElements annotation states that the possible node names are foo and bar.
package forum10453441;
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlType(propOrder={"nullList", "emptyList", "emptyChoiceList"})
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
private List<String> nullList = null;
private List<String> emptyList = new ArrayList<String>();
#XmlElements({
#XmlElement(name="foo", type=String.class),
#XmlElement(name="bar", type=String.class)
})
private List<String> emptyChoiceList = new ArrayList<String>();
}
Demo
The following code demonstrates how to specify the MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS property.
package forum10453441;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
marshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, true);
Root root = new Root();
marshaller.marshal(root, System.out);
}
}
Output
Now in the output the empty lists are marshalled as empty arrays. For the field that was mapped with #XmlElememts the node name that was specified was used in the JSON representation.
{
"emptyList" : [ ],
"foo" : [ ]
}

Resources