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" : [ ]
}
Related
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
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>
We noticed in production at a customer site today that an ArrayList marshals fine under Java 6-invoked JAXB and fails under Java 7-invoked JAXB. Specifically, a property of ours that is defined as a List that actually returns the result of an ArrayList#subList(int, int) call causes a marshaling exception when we run our code using Java 7.
This has to do with the fact that the innards of ArrayList have changed dramatically. One of the things that has apparently changed is that ArrayList#subList(int, int) now returns a new inner class (called SubList). When this class is encountered by JAXB, well, boom. Here is a portion of the stack we're seeing:
Failed to create a new JAXBInstance.
com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
java.util.ArrayList$SubList is a non-static inner class, and JAXB can't handle those.
this problem is related to the following location:
at java.util.ArrayList$SubList
Given that marshalling this used to work under Java 6, what is the suggested workaround here?
this problem is noticed in our place, and we had to convert sublist into an AbstractPage (which is our own collection type with pagination support) collection & get serialization done.
When I run the following on JDK 1.7.0_45 for the Mac everything works for me. Is this equivalent to what you are doing?
Java Model
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Foo {
private ArrayList<String> bar;
public Foo() {
bar = new ArrayList<String>(3);
bar.add("A");
bar.add("B");
bar.add("C");
bar.add("D");
}
#XmlElement
public List<String> getBarSubset() {
return bar.subList(1, 3);
}
}
Demo
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Foo foo = new Foo();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo>
<barSubset>B</barSubset>
<barSubset>C</barSubset>
</foo>
I've tried the given test in JDK7 and in standalone latest JAXB - no failers.
I have the following class
#XmlRootElement
public class Test {
#XmlTransient
private String abc;
#XmlElement
private String def;
}
My question is, I want to use this class to generate two kinds of XMLs
1. With <abc>
2. without <abc>
I can achieve the second one since I have marked it as transient. Is there any way so that if I mark "abc" as #XMLElement and can ignore it while marshalling?
Thanks in advance
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
You may be interested in the #XmlNamedObjectGraph extension we have added in EclipseLink 2.5.0. It allows you to define multiple views on your domain model. You can try this out today using a nightly build:
http://www.eclipse.org/eclipselink/downloads/nightly.php
Below I'll give an example:
Test
The #XmlNamedObjectGraph annotation is used to define subsets of the object graph that can be used when marshalling and unmarshalling.
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;
#XmlNamedObjectGraph(
name="only def",
attributeNodes = {
#XmlNamedAttributeNode("def")
}
)
#XmlRootElement
public class Test {
private String abc;
private String def;
public String getAbc() {
return abc;
}
public void setAbc(String abc) {
this.abc = abc;
}
public String getDef() {
return def;
}
public void setDef(String def) {
this.def = def;
}
}
Demo
The MarshallerProperties.OBJECT_GRAPH can be used to specify which object graph should be marshallled.
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(Test.class);
Test test = new Test();
test.setAbc("FOO");
test.setDef("BAR");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// Marshal the Entire Object
marshaller.marshal(test, System.out);
// Marshal Only What is Specified in the Object Graph
marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "only def");
marshaller.marshal(test, System.out);
}
}
Output
Below is the output from running the demo code. The first time the instance of Test is marshalled it contains all the properties and the second time just the def property.
<?xml version="1.0" encoding="UTF-8"?>
<test>
<abc>FOO</abc>
<def>BAR</def>
</test>
<?xml version="1.0" encoding="UTF-8"?>
<test>
<def>BAR</def>
</test>
For More Information
http://blog.bdoughan.com/2013/03/moxys-object-graphs-inputoutput-partial.html
http://blog.bdoughan.com/2013/03/moxys-object-graphs-partial-models-on.html
I'm upgrading a Java object that currently has XML representation in this spirit:
<myObjects>
<myObject uid="42" type="someEnum">
<name>Waldo</name>
<description>yada yada</description>
<myElement>some_string</myElement>
...
</myObject>
...
</myObjects>
myElement is optional - it can be null in Java / omitted in XML.
I'm adding a field that is only relevant if myElement has an actual value
(and to keep compatibility with previous XML, it's optional in itself)
I'm trying to avoid this:
<myElement>some_string</myElement>
<myAttr>foo</myAttr>
and prefer something like this:
<myElement myAttr="foo">some_string</myElement>
but banging my head for 2 days now on how to annotate it.
I thought of marking it with XmlTransient and let an XmlAnyElement catch it instead while unmarshalling - but it seems this will cause a problem when marshalling the object back from Java to XML.
I tried creating an XmlAdapter for the element - but the unmarshal method gets only the inner content ("some_string"). Am I missing something with this technique?
Is there a way to just get the element as a string ("<myElement myAttr=\"foo\">some_string</myElement>") and I will process it myself?
Do you recommend any other approach?
You can use the EclipseLink JAXB (MOXy) #XmlPath extension to easily accomplish this:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class MyObject {
#XmlAttribute
private int uid;
#XmlAttribute
private String type;
private String name;
private String description;
private String myElement;
#XmlPath("myElement/#myAttr")
private String myAttr;
}
This class will interact with the following XML:
<myObject uid="42" type="someEnum">
<name>Waldo</name>
<description>yada yada</description>
<myElement myAttr="foo">some_string</myElement>
</myObject>
Using the following demo code:
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(MyObject.class);
File file = new File("input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
MyObject myObject = (MyObject) unmarshaller.unmarshal(file);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(myObject, System.out);
}
}
To specify MOXy as your JAXB implementation you need to include a file called jaxb.properties in the same package as your model classes with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
For more information on MOXy's XPath based mapping see:
http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html
http://bdoughan.blogspot.com/2010/07/xpath-based-mapping.html
The answer was dead simple: I'm so used to annotate with XmlElement and XmlAttribute, that I forgot about XmlValue!
The code for MyObject is the same as in Blaise Doughan answer, with one change:
myElement is now a class (ElemWithAttr) instead of String:
public class ElemWithAttr {
#XmlValue
public String content;
#XmlAttribute
public String myAttr;
}