I am having an issue with exporting a custom hashmap function to xml. The layout of my project is as follows. The package adt contains the following classes:
class HashMap implements Map<K,V>
A custom hashmap implementation, pretty much identical to java.util.HashMap. (Note, same errors exist when I use java.util.HashMap)
class Database extends HashMap <String,Table>
class Table extends HashMap <Object, Row>
(Note: The key of type Object is always a string in my implementation)
class Row extends HashMap <String, Object>
(Object is of type String, boolean, or int)
I am trying to export a table to an xml file, and eventually import it again but I haven't gotten that far. Here is the function which I use to export it:
private Response xml(Table table, FileWriter file) {
JAXBContext jaxbContext;
Marshaller jaxbMarshaller;
try {
jaxbContext = JAXBContext.newInstance(HashMap.class);
jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(table, file);
}catch (JAXBException e) {
e.printStackTrace(System.out);
return new Response(false,"Export - JAXBException.",null);
}//catch
return new Response(true,"Export - Successfully wrote table to file.",null);
}//xml
Also note...
- I can export flawlessly using JsonObjectBuilder from javax.json.* to a json file
- It is required to use the javax.xml library
- I really don't know what I'm doing
It returns the code:
?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<hashMap/>
The table which I am trying to export does have data which is not represented in the XML output. Right now, the HashMap class has #XmlRootElement at the top. That is the only XML annotation in any class.
I also receive a NullPointerException when I change this line
jaxbContext = JAXBContext.newInstance(HashMap.class);
to this:
jaxbContext = JAXBContext.newInstance(Table.class);
The exception is as follows:
Exception in thread "main" java.lang.NullPointerException
at com.sun.xml.internal.bind.v2.runtime.property.SingleMapNodeProperty.serializeBody(SingleMapNodeProperty.java:252)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:345)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:578)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:326)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:479)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:308)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:236)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:95)
at driver.DExport.xml(DExport.java:99)
at driver.DExport.execute(DExport.java:59)
at core.Server.interpret(Server.java:57)
at core.Console.prompt(Console.java:56)
at core.Console.main(Console.java:37)
After much trial and error, it so turns out that I needed to create a list of Row extends HashMap<String,Object> types. I guess the fact that there was nested hashmapping (Table extends Hashmap<Object,Row extends Hashmap<String,Object>> screwed it up. Now, my Row class contains #XmlRootElement, and I made my Tuple(known as Entry in java.util.Hashmap) static, with the get methods for key and val having the #XmlElement annotation. Now I must export in a loop. Pretty sure this is the wrong way to do it but it works(kinda)...
jaxbContext = JAXBContext.newInstance(Row.class);
jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
for (int i = 0; i<rows.size(); i++) {
jaxbMarshaller.marshal(rows.get(i), file);
}//for
and the output...
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<row>
<Tuple>
<key xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">str</key>
<val xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">goodbye</val>
</Tuple>
<Tuple>
<key xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">bool</key>
<val xsi:type="xs:boolean" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">false</val>
</Tuple>
</row>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<row>
<Tuple>
<key xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">str</key>
<val xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">hello</val>
</Tuple>
<Tuple>
<key xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">bool</key>
<val xsi:type="xs:boolean" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">true</val>
</Tuple>
</row>
Hi I am currently using jaxb to get my model saved to xml . My model I have one string and one hashmap. So the problem here is while exporting the hashmap to xml i am getting something like this .
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<addressMap>
<entry>
<key>col2</key>
<value>data2</value>
</entry>
<entry>
<key>col1</key>
<value>data1</value>
</entry>
</addressMap>
</customer>
SO here i donot want this entry tag and key instead of that something like below xml I expect..
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<addressMap>
<col2>data2</col2>
<col1>data1</col1>
</addressMap>
</customer>
Is it possible to achieve this
Almost. I would like to suggest a change to the xml-format. Using element names like col1, col2, etc is a "bad" idea. It's not well structured. If you can accept the following format of the xml data I can give you an example on how:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<addressMap>
<col key="col2">data2</col>
<col key="col1">data1</col>
</addressMap>
</customer>
I guess you had a class defined something like this:
#XmlRootElement
public class Customer {
#XmlElement
public Map<String,String> addressMap;
}
When marshaled with JAXB it should produce your first output. Change it to the following and add the necessary classes:
#XmlRootElement
public class Customer {
#XmlElement
public AddressMap addressMap;
}
public class AddressMap {
#XmlElement
public List<Column> col;
}
public class Column {
#XmlAttribute
public String key;
#XmlValue
public String value;
}
Fill it with your data and marshal it and the output should look like my xml example.
EDIT:
Keeping addressMap as a HashMap:
Make Customer class look like this:
#XmlRootElement
public class Customer {
#XmlElement
#XmlJavaTypeAdapter(MapAdapter.class)
public Map<String,String> addressMap;
}
and create the class MapAdapter:
public class MapAdapter extends XmlAdapter<AddressMap, Map<String,String>> {
#Override
public AddressMap marshal(Map<String,String> map) throws Exception {
AddressMap myMap = new AddressMap();
myMap.col = new ArrayList<Column>();
for (Entry<String,String> entry : map.entrySet()) {
Column col = new Column();
col.key = entry.getKey();
col.value = entry.getValue();
myMap.col.add(col);
}
return myMap;
}
#Override
public Map<String,String> unmarshal(AddressMap myMap) throws Exception {
Map<String,String> map = new HashMap<String, String>();
for (Column col : myMap.col) {
map.put(col.key, col.value);
}
return map;
}
}
Keep classes AddressMap and Column as is.
I have to convert xml to Map<String,String>. I have following XML structure:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Environments>
<Environment Name="A" URIPath="http://a.com" />
<Environment Name="B" URIPath="http://b.com" />
<Environment Name="C" URIPath="http://c.com" />
</Environments>
I tried multiple ways but eneded with Class has two properties of the same name "URIPath". What is the right design for unmarshalling this XML?
UPDATE:
Using provided solution #1 I am getting :
Class has two properties of the same name "environments"
this problem is related to the following location:
at public java.util.List app.model.Environments.getEnvironments()
at app.model.Environments
this problem is related to the following location:
at public java.util.List app.model.Environments.environments
at app.model.Environments
Class has two properties of the same name "URIPath"
this problem is related to the following location:
at public java.lang.String app.model.Environment.getURIPath()
at app.model.Environment
at public java.util.List app.model.Environments.environments
at app.model.Environments
this problem is related to the following location:
at java.lang.String app.model.Environment.URIPath
at app.model.Environment
at public java.util.List app.model.Environments.environments
at app.model.Environments
] with root cause
You have 2 options:
1) Unmarshal a collection of Environment instances with 2 fields: Name and URIPath. You can build the map later if you want to from the collection.
2) Use a custom XmlAdapter which properly creates the map from the collection.
Elaborating Solution #1
This solution needs the following classes:
class Environments {
#XmlElement(name = "Environment")
public List<Environment> environments;
}
class Environment {
#XmlAttribute(name = "Name")
public String Name;
#XmlAttribute(name = "URIPath")
public String URIPath;
}
And using these, unmarhaling:
Environments environments = JAXB.unmarshal(new File("env.xml"),
Environments.class);
Elaborating Solution #2
If you want to use a custom XmlAdapter to directly get a Map, the XML input in its current form cannot be used. It has to be slightly modified to put a wrapper XML element around it. This is required because in Java the Map is a property of a class but the <Environments> tag is just the wrapper for the Map. Example modified XML:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<wrapper>
<Environments>
<Environment Name="A" URIPath="http://a.com" />
<Environment Name="B" URIPath="http://b.com" />
<Environment Name="C" URIPath="http://c.com" />
</Environments>
</wrapper>
Taking this as the input XML, here is the solution:
class EnvironmentMap {
#XmlJavaTypeAdapter(value = EnvMapAdapter.class)
#XmlElement(name = "Environments")
public Map<String, String> envMap;
}
class Environments {
#XmlElement(name = "Environment")
public List<Environment> environments;
}
class Environment {
#XmlAttribute(name = "Name")
public String name;
#XmlAttribute(name = "URIPath")
public String uriPath;
}
class EnvMapAdapter extends XmlAdapter<Environments, Map<String, String>> {
#Override
public Map<String, String> unmarshal(Environments envs) throws Exception {
Map<String, String> map = new HashMap<>();
for (Environment env : envs.environments)
map.put(env.name, env.uriPath);
return map;
}
#Override
public Environments marshal(Map<String, String> map) throws Exception {
Environments environments = new Environments();
// This method is only called if you marshal (Java -> XML)
environments.environments = new ArrayList<>(map.size());
for (Entry<String, String> entry : map.entrySet()) {
Environment e = new Environment();
e.name = entry.getKey();
e.uriPath = entry.getValue();
environments.environments.add(e);
}
return environments;
}
}
And using it:
EnvironmentMap envMap = JAXB.unmarshal(new File("env2.xml"),
EnvironmentMap.class);
System.out.println(envMap.envMap);
Which prints:
{A=http://a.com, B=http://b.com, C=http://c.com}
I'm using JAXB and I have a problem.
I have an Element with a dynamic number of properties. As a result, the structure of its equivalent XML structure will not be static. This is an example of the desired XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<element>
<property1>value 1</property1>
<property2>value 2</property2>
<property3>value 3</property3>
<propertyn>value n</propertyn>
</element>
To generate this, I used #XmlAnyElement like this:
#XmlRootElement(name = "element")
public class Element {
private Map<String, String> properties = new HashMap<String, String>();
#XmlTransient
public Map<String, String> getProperties() {
return properties;
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
#XmlAnyElement
public List<JAXBElement<String>> getElements() {
List<JAXBElement<String>> elements = new ArrayList<JAXBElement<String>>();
for (String property: properties.keySet()) {
JAXBElement<String> jaxbElement = new JAXBElement<String>(new QName(property), String.class, properties.get(property));
elements.add(jaxbElement);
}
return elements;
}
}
This works fine! However, I'm trying to add nested elements to support multi-value properties, so that the XML will be like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<element>
<property1>value 1</property1>
<property2>value 2</property2>
<property3>value 3</property3>
<property4>
<value>value 4a</value>
<value>value 4b</value>
<value>value 4c</value>
</property4>
<propertyn>value n</propertyn>
</element>
Please note that:
The number of properties is not static.
The number of multi-value properties is not static.
The number of values of each multi-value property is not static.
My question is, how can I do that in a simple way? Is #XmlElementWrapper useful in my case?
Thanks!
I'm trying to create a very simple REST server. I just have a test method that will return a List of Strings. Here's the code:
#GET
#Path("/test2")
public List test2(){
List list=new Vector();
list.add("a");
list.add("b");
return list;
}
It gives the following error:
SEVERE: A message body writer for Java type,
class java.util.Vector, and MIME media type,
application/octet-stream, was not found
I was hoping JAXB had a default setting for simple types like String, Integer, etc. I guess not. Here's what I imagined:
<Strings>
<String>a</String>
<String>b</String>
</Strings>
What's the easiest way to make this method work?
I used #LiorH's example and expanded it to:
#XmlRootElement(name="List")
public class JaxbList<T>{
protected List<T> list;
public JaxbList(){}
public JaxbList(List<T> list){
this.list=list;
}
#XmlElement(name="Item")
public List<T> getList(){
return list;
}
}
Note, that it uses generics so you can use it with other classes than String. Now, the application code is simply:
#GET
#Path("/test2")
public JaxbList test2(){
List list=new Vector();
list.add("a");
list.add("b");
return new JaxbList(list);
}
Why doesn't this simple class exist in the JAXB package? Anyone see anything like it elsewhere?
#GET
#Path("/test2")
public Response test2(){
List<String> list=new Vector<String>();
list.add("a");
list.add("b");
final GenericEntity<List<String>> entity = new GenericEntity<List<String>>(list) { };
return Response.ok().entity(entity).build();
}
In case anyone of you wants to write a list wrapper for lists containing elements of multiple classes and want to give an individual XmlElement name according to the Class type without Writing X Wrapper classes you could use the #XmlMixed annotation.
By doing so JAXB names the items of the list according to the value set by the #XmlRootElement.
When doing so you have to specify which classes could possibly be in the list using #XmlSeeAlso
Example:
Possible Classes in the list
#XmlRootElement(name="user")
public class User {/*...*/}
#XmlRootElement(name="entry")
public class LogEntry {/*...*/}
Wrapper class
#XmlRootElement(name="records")
#XmlSeeAlso({User.class, LogEntry.class})
public static class JaxbList<T>{
protected List<T> records;
public JaxbList(){}
public JaxbList(List<T> list){
this.records=list;
}
#XmlMixed
public List<T> getRecords(){
return records;
}
}
Example:
List l = new List();
l.add(new User("userA"));
l.add(new LogEntry(new UserB()));
XStream xStream = new XStream();
String result = xStream.toXML(l);
Result:
<records>
<user>...</user>
<entry>...</entry>
</records>
Alternatevily you could specify the XmlElement names directly inside the wrapper class using the #XmlElementRef annotation
#XmlRootElement(name="records")
#XmlSeeAlso({User.class, LogEntry.class})
public static class JaxbList<T>{
protected List<T> records;
public JaxbList(){}
public JaxbList(List<T> list){
this.records=list;
}
#XmlElementRefs({
#XmlElementRef(name="item", type=Object.class),
#XmlElementRef(name="user", type=User.class),
#XmlElementRef(name="entry", type=LogEntry.class)
})
public List<T> getRecords(){
return records;
}
}
From a personal blog post, it is not necessary to create a specific JaxbList < T > object.
Assuming an object with a list of strings:
#XmlRootElement
public class ObjectWithList {
private List<String> list;
#XmlElementWrapper(name="MyList")
#XmlElement
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
}
A JAXB round trip:
public static void simpleExample() throws JAXBException {
List<String> l = new ArrayList<String>();
l.add("Somewhere");
l.add("This and that");
l.add("Something");
// Object with list
ObjectWithList owl = new ObjectWithList();
owl.setList(l);
JAXBContext jc = JAXBContext.newInstance(ObjectWithList.class);
ObjectWithList retr = marshallUnmarshall(owl, jc);
for (String s : retr.getList()) {
System.out.println(s);
} System.out.println(" ");
}
Produces the following:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectWithList>
<MyList>
<list>Somewhere</list>
<list>This and that</list>
<list>Something</list>
</MyList>
</objectWithList>
This can be done MUCH easier using wonderful XStream library. No wrappers, no annotations.
Target XML
<Strings>
<String>a</String>
<String>b</String>
</Strings>
Serialization
(String alias can be avoided by using lowercase string tag, but I used OP's code)
List <String> list = new ArrayList <String>();
list.add("a");
list.add("b");
XStream xStream = new XStream();
xStream.alias("Strings", List.class);
xStream.alias("String", String.class);
String result = xStream.toXML(list);
Deserialization
Deserialization into ArrayList
XStream xStream = new XStream();
xStream.alias("Strings", ArrayList.class);
xStream.alias("String", String.class);
xStream.addImplicitArray(ArrayList.class, "elementData");
List <String> result = (List <String>)xStream.fromXML(file);
Deserialization into String[]
XStream xStream = new XStream();
xStream.alias("Strings", String[].class);
xStream.alias("String", String.class);
String[] result = (String[])xStream.fromXML(file);
Note, that XStream instance is thread-safe and can be pre-configured, shrinking code amount to one-liners.
XStream can also be used as a default serialization mechanism for JAX-RS service. Example of plugging XStream in Jersey can be found here
I have encountered this pattern a few times, I found that the easiest way is to define an inner class with JaxB annotations. (anyways, you'll probably want to define the root tag name)
so your code would look something like this
#GET
#Path("/test2")
public Object test2(){
MyResourceWrapper wrapper = new MyResourceWrapper();
wrapper .add("a");
wrapper .add("b");
return wrapper ;
}
#XmlRootElement(name="MyResource")
private static class MyResourceWrapper {
#XmlElement(name="Item")
List<String> list=new ArrayList<String>();
MyResourceWrapper (){}
public void add(String s){ list.add(s);}
}
if you work with javax.rs (jax-rs) I'd return Response object with the wrapper set as its entity
Finally I've solved it using JacksonJaxbJsonProvider It requires few changes in your Spring context.xml and Maven pom.xml
In your Spring context.xml add JacksonJaxbJsonProvider to the <jaxrs:server>:
<jaxrs:server id="restService" address="/resource">
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
</jaxrs:providers>
</jaxrs:server>
In your Maven pom.xml add:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.0</version>
</dependency>
User1's example worked well for me. But, as a warning, it won't work with anything other than simple String/Integer types, unless you add an #XmlSeeAlso annotation:
#XmlRootElement(name = "List")
#XmlSeeAlso(MovieTicket.class)
public class MovieTicketList {
protected List<MovieTicket> list;
This works OK, although it prevents me from using a single generic list class across my entire application. It might also explain why this seemingly obvious class doesn't exist in the JAXB package.
Make sure to add #XmlSeeAlso tag with your specific classes used inside JaxbList. It is very important else it throws HttpMessageNotWritableException
I would've saved time if I found Resteasy Jackson Provider sooner.
Just add the Resteasy Jackson Provider JAR. No entity wrappers. No XML annotations. No custom message body writers.
If you are using maven in the jersey project add below in pom.xml and update project dependencies so that Jaxb is able to detect model class and convert list to Media type application XML:
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.11</version>
</dependency>
For a more general solution, for JAXB-XML serialization of any top level list , which only requires 1 new class to be written, check out the solution given in this question:
Is it possible to programmatically configure JAXB?
public class Wrapper<T> {
private List<T> items = new ArrayList<T>();
#XmlAnyElement(lax=true)
public List<T> getItems() {
return items;
}
}
//JAXBContext is thread safe and so create it in constructor or
//setter or wherever:
...
JAXBContext jc = JAXBContext.newInstance(Wrapper.class, clazz);
...
public String marshal(List<T> things, Class clazz) {
//configure JAXB and marshaller
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
//Create wrapper based on generic list of objects
Wrapper<T> wrapper = new Wrapper<T>(things);
JAXBElement<Wrapper> wrapperJAXBElement = new JAXBElement<Wrapper>(new QName(clazz.getSimpleName().toLowerCase()+"s"), Wrapper.class, wrapper);
StringWriter result = new StringWriter();
//marshal!
m.marshal(wrapperJAXBElement, result);
return result.toString();
}