I have a class that I wish to populate with content from an XML file using JAXB. My XML file looks similar to this:
<root>
<mylist>
<item id="1">First Item</item>
<item id="2">Second Item</item>
</mylist>
</root>
My JAXB annotated classes look like:
#XmlRootElement
class MyParentClass {
// I always populate this with a TreeSet
private Set<MyFieldItem> items;
public void setItems(Set<MyFieldItem> items) {
this.items = items;
}
#XmlElementWrapper("mylist") #XmlElement("item")
public Set<MyFieldItem> getItems() {
return items;
}
}
class MyFieldItem implements Comparable<MyFieldItem> {
private Integer id;
private String value;
public void setId(Integer id) {
this.id = id;
}
#XmlAttribute
public Integer getId() {
return id;
}
public void setValue(String value) {
this.value = value;
}
#XmlValue
public String getValue() {
return value;
}
public int compareTo(MyfieldItem o) {
return this.id.compareTo(o.getId());
}
}
I find that this arrangement serialises my objects to XML correctly, but when I try to convert it back the TreeSet I use becomes a HashSet.
In theory my collection could be fixed to a TreeSet (which does fix the problem), but I'd rather configure JAXB correctly and defer that logic elsewhere. How do I tell JAXB to build a TreeSet instead?
The easiest way to solve this problem is pre-initialize your Set property to the appropriate implementation type, and your JAXB implmentation (Metro, EclipseLink MOXy, Apache JaxMe, etc) will use that instead of creating a new Set:
package forum7104810;
import java.util.Set;
import java.util.TreeSet;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="root")
class MyParentClass {
// Initialize this property with an instance of the desired type.
private Set<MyFieldItem> items = new TreeSet<MyFieldItem>();
public void setItems(Set<MyFieldItem> items) {
this.items = items;
}
#XmlElementWrapper(name="mylist")
#XmlElement(name="item")
public Set<MyFieldItem> getItems() {
return items;
}
}
For More Information
http://blog.bdoughan.com/2011/01/jaxb-and-choosing-list-implementation.html
Related
I am trying to serialize the below ArrayList of GAccount objects using Jackson library with following code:
List<Gaccount> gAccounts;
ObjectMapper mapper=new ObjectMapper();
json=mapper.writeValueAsString(gAccounts);
However, I have noticed that only Id and Name fields are serialized but not properties. Sorry, but I am new to Jackson library. Do I have to manually serialize that field ?
package in.co.madhur.ganalyticsdashclock;
import java.util.ArrayList;
import java.util.List;
public class GAccount
{
private String Id;
private String Name;
private List<GProperty> properties=new ArrayList<GProperty>();
public GAccount(String Id, String Name)
{
this.Id=Id;
this.Name=Name;
}
public String getName()
{
return Name;
}
public void setName(String name)
{
Name = name;
}
public String getId()
{
return Id;
}
public void setId(String id)
{
Id = id;
}
List<GProperty> getProperties()
{
return properties;
}
void setProperties(List<GProperty> properties)
{
this.properties = properties;
}
#Override
public String toString()
{
return Name;
}
}
The default visibility is to use all public getter methods and all public properties. If you make the getter this:
public List<GProperty> getProperties()
it should work.
You could also change the auto-detection defaults, but it's overkill here. See http://www.cowtowncoder.com/blog/archives/2011/02/entry_443.html for more info.
I am using jackson 2.9.0. The default visibility is always 'false' to all the members. In this case, we alway need to use a different visibility, otherwise the result json string will be empty. Here is the code extracted from JsonAutoDetect
public boolean isVisible(Member m) {
switch(this) {
case ANY:
return true;
...
case PUBLIC_ONLY:
return Modifier.isPublic(m.getModifiers());
default:
return false;
}
}
When trying to unmarshall this xml:
<holder>
<name>a</name>
<elements>
<element>
<name>elem</name>
</element>
</elements>
</holder>
I get the error unexpected element (uri:"", local:"element"). Expected elements are <{}link>,<{}totalSize> in the ValidationEventHandler and the tag <elements> (and therefore the elements field in Holder class) is ignored.
When generating the XML both link and totalSize are not outputted as they are nil.
JAVA MODEL
The hierarchy is a bit complex:
(Simplified for the sake of the question)
ElementRoot
abstract ElementRoot has the link member
public abstract class ElementRoot implements Serializable {
protected String link;
#XmlElement(name = "link")
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
}
Wrapper
abstract Wrapper extends ElementRoot and has the totalSize member
public abstract class Wrapper<T> extends ElementRoot {
protected int totalSize;
protected List<T> collection = new ArrayList<>();
#XmlElement
public int getTotalSize() {
return totalSize;
}
public void setTotalSize(int totalSize) {
this.totalSize = totalSize;
}
public abstract List<T> getCollection();
}
Holder
Holder extends ElementRoot
#XmlRootElement(name = "holder")
#XmlType(propOrder = {"name", "elements"})
public class Holder extends ElementRoot {
private String name;
private Elements elements;
// setters and getters not annotated
}
Elements
Elements extends Wrapper and has a collection of Element
import java.util.Collection;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "elements)
public class Elements extends Wrapper {
#Override
#XmlElement(name="element")
public Collection<Element> getElements() {
return elements;
}
// No setter, to add getElements().add(element)
}
Element
Element extends ElementRoot
#XmlRootElement(name = "element")
#XmlType(propOrder = {"id", "name"})
public class Element extends ElementRoot {
private Integer id;
private String name;
// setters and getters no annotated
}
ENVIRONMENT
I'm using java 7:
JAXB-api 2.2.7
MOXy 2.5.0
There appears to be a bug in EclipseLink JAXB (MOXy) for this use case related to the abstract getCollecion property. We have opened up the following bug that you can use to track our progress on this issue:
http://bugs.eclipse.org/411408
WORK AROUND
Wrapper
We can use #XmlAccessorType(XmlAccessType.NONE) so that only annotated fields/properties will be processed (see: http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html).
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.NONE)
public abstract class Wrapper<T> extends ElementRoot {
protected int totalSize;
protected List<T> collection = new ArrayList<>();
#XmlElement
public int getTotalSize() {
return totalSize;
}
public void setTotalSize(int totalSize) {
this.totalSize = totalSize;
}
public abstract List<T> getCollection();
}
Elements
Since #XmlAccessorType is inherited by the subclasses we will specify XmlAccessType.PUBLIC to return things to normal. Note: I assume the getElements() method in your question should have been getCollection().
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "elements")
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
public class Elements extends Wrapper {
#Override
#XmlElement(name="element")
public List<Element> getCollection() {
return collection;
}
// No setter, to add getElements().add(element)
}
I was trying to figure out if it is possible to unmarshall an xml element to multiple pojos. for example:
for xml:
<type>
<id>1</id>
<cost>12</cost>
<height>15</height>
<width>13</width>
<depth>77</depth>
</type>
Item class
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlRootElement(name="type")
public class Item {
private Integer id;
private Double cost;
#XmlElement(name="id")
public Integer getId(){
return id;
}
#XmlElement(name="cost")
public Double getCost(){
return cost
}
}
ItemDimensions Class
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlRootElement(name="type")
public class ItemDimensions {
private Integer height;
private Integer width;
private Integer depth;
#XmlElement(name="height")
public Integer getHeight(){
return height;
}
#XmlElement(name="width")
public Integer getWidth(){
return width;
}
#XmlElement(name="depth")
public Integer getDepth(){
return depth;
}
}
I have tried to accomplish something similar using a number of JAXB mappings generated by Netbeans 6.9 and a number of test classes but have gotten nowhwere. Does anyone know if this is something that can be done without any intermediary objects?
You could use the #XmlPath extension in EclipseLink JAXB (MOXy) to accomplish this use case (I'm the MOXy tech lead):
Root
JAXB requires a single object to unmarshal, we will introduce a class to fulfill this role. This class will have fields corresponding to the two Objects you wish to unmarshal annotated with the self XPath: #XmlPath(".")
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="type")
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlPath(".")
private Item item;
#XmlPath(".")
private ItemDimensions itemDimensions;
}
ItemDimensions
You annotate this class normally. In your example you annotate the properties, but only provide getters. This will cause JAXB to think that those are write only mappings.
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class ItemDimensions {
private Integer height;
private Integer width;
private Integer depth;
}
Item
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Item {
private Integer id;
private Double cost;
}
Demo
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(Root.class);
Unmarshaller u = jc.createUnmarshaller();
Object o = u.unmarshal(new File("input.xml"));
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(o, System.out);
}
}
jaxb.properties
To use MOXy as your JAXB implementation, you must provide a file named jaxb.properties in with your domain objects with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
I've read a time ago about generate xml from Java using annotations, but I'm not finding a simple example now.
If I want to make a xml file like:
<x:element uid="asdf">value</x:element>
from my java class:
public class Element {
private String uid = "asdf";
private String value = "value";
}
Which annotations should I use to perform that? (I have a xml-schema, if this helps the generation)
--update
The javax.xml.bind.annotation package have the annotations, "but I still haven't found what I'm looking for": an exemple of usage.. :)
Found it:
import java.io.FileOutputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlRootElement;
public class JavaToXMLDemo {
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Employee.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Employee object = new Employee();
object.setCode("CA");
object.setName("Cath");
object.setSalary(300);
m.marshal(object, System.out);
}
}
#XmlRootElement
class Employee {
private String code;
private String name;
private int salary;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int population) {
this.salary = population;
}
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
<code>CA</code>
<name>Cath</name>
<salary>300</salary>
</employee>
From: http://www.java2s.com/Code/JavaAPI/javax.xml.bind.annotation/javaxxmlbindannotationXmlRootElement.htm
For the benefit of anyone else hitting this thread, I imagine you did the following:
#XmlRootElement
public class Element {
#XmlAttribute
private String uid = "asdf";
#XmlValue
private String value = "value";
}
For More Information
http://bdoughan.blogspot.com/2011/06/jaxb-and-complex-types-with-simple.html
There are various tools that you can use to do this. XStream (http://x-stream.github.io/) is a reasonably easy tool to use that allows you to use annotations to determine the schema of XML that is created.
I would like to marshall a Collection as nested attributes.
Right now I have:
#XmlElement(name="entry")
public Collection<Integer> getSizes(){ ... }
which returns:
<entry>1</entry>
<entry>2</entry>
But I would like to get:
<entry id="1"/>
<entry id="2"/>
Is this possible without new classes?
Seems to be impossible without new classes at all. Use XmlAdapter:
class EntryAdapter extends XmlAdapter<EntryAdapter.Entry, Integer>
{
public EntryAdapter.Entry marshal(Integer id) {
return new Entry(id);
}
public Integer unmarshal(Entry e) {
return e.getId();
}
static class Entry
{
private Integer id;
public Entry() {}
public Entry(Integer id) { this.id = id; }
#XmlAttribute
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
}
}
-
#XmlElement(name="entry")
#XmlJavaTypeAdapter(EntryAdapter.class)
public Collection<Integer> getSizes(){ ... }
As the accepted answer says, XmlAdapter is the standard JAXB solution.
But if you are using EclipseLink MOXy as your JAXB provider and can use one of its extensions, namely #XmlPath, it can be used to achieve the same result.
To marshal the collection values as attributes, you use it like this:
#XmlPath("entry/#id")
public Collection<Integer> getSizes(){ ... }