I am having troubles unmarshalling some xml using the XStream library. The related java class uses the java.util.Collection class in order to store some attributes, which I understand is a problem for XStream. However, I am unable to change the Java class to use something like ArrayList due to various reasons. Is there a way to unmarshal the xml using XStream, or should I search other libraries for a solution?
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamImplicit;
import org.testng.annotations.Test;
import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class ControllerTest {
#XStreamAlias("controllers")
public class ControllerList implements Serializable {
#XStreamImplicit(
itemFieldName = "controller"
)
private List<Controller> controllers = new ArrayList();
public ControllerList() {
}
public List<Controller> getControllers() {
return this.controllers;
}
public void setControllers(List<Controller> controllers) {
this.controllers = controllers;
}
}
#XStreamAlias("controller")
public class Controller extends BasicInfo {
#XStreamImplicit(
itemFieldName = "storageInfo"
)
private Collection<BasicInfo> storage;
public Controller() {
}
public Collection<BasicInfo> getStorage() {
return this.storage;
}
public void setStorage(Collection<BasicInfo> storage) {
this.storage = storage;
}
}
#XStreamAlias("basicinfo")
public class BasicInfo{
private String name;
public BasicInfo() {
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
#Test(groups = {"edge"})
public void testControllers() {
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><controllers><controller><storageInfo>" +
"<name>My name</name></storageInfo></controller></controllers>";
XStream stream = new XStream();
stream.processAnnotations(ControllerList.class);
InputStream in = new ByteArrayInputStream(xml.getBytes());
try {
InputStreamReader rdr = new InputStreamReader(in, "UTF-8");
ControllerList controllers = (ControllerList) stream.fromXML(rdr);
} catch (UnsupportedEncodingException e) {
}
}
}
XStream CollectionConverter does not supports java.util.Collection. So, you can try in two ways:
replace Collection by List:
import java.util.List;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamImplicit;
#XStreamAlias("controller")
public class Controller {
#XStreamImplicit(itemFieldName = "storageInfo")
private List<BasicInfo> storage;
public List<BasicInfo> getStorage() {
return storage;
}
public void setStorage(final List<BasicInfo> storage) {
this.storage = storage;
}
}
This test should work for the first case:
#Test
public void testControllers() {
final String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><controllers><controller><storageInfo><name>My name</name></storageInfo></controller></controllers>";
final XStream stream = new XStream();
stream.processAnnotations(ControllerList.class);
final ControllerList controllers = (ControllerList) stream.fromXML(xml);
final List<Controller> colls = controllers.getControllers();
Assert.assertEquals(colls.size(), 1);
final Controller coll = colls.get(0);
final List<BasicInfo> infos = coll.getStorage();
Assert.assertEquals(infos.size(), 1);
final BasicInfo info = infos.get(0);
Assert.assertEquals(info.getName(), "My name");
}
Add a default implementation to java.util.Collection. This test should work:
#Test
public void testControllers() {
final String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><controllers><controller><storageInfo><name>My name</name></storageInfo></controller></controllers>";
final XStream stream = new XStream();
stream.processAnnotations(ControllerList.class);
stream.addDefaultImplementation(ArrayList.class, Collection.class);
final ControllerList controllers = (ControllerList) stream.fromXML(xml);
final List<Controller> colls = controllers.getControllers();
Assert.assertEquals(colls.size(), 1);
final Controller coll = colls.get(0);
final Collection<BasicInfo> infos = coll.getStorage();
Assert.assertEquals(infos.size(), 1);
for (final BasicInfo info : infos) {
Assert.assertEquals(info.getName(), "My name");
}
}
Related
Hello I have Java Bean like blow and I need to create xml file with it , but when run the program error say :
mehritco.ir.megnatis.ejb.nlp.aiml.Aiml$Template$Random is a non-static inner class, and JAXB can't handle those.
this problem is related to the following location:
at mehritco.ir.megnatis.ejb.nlp.aiml.Aiml$Template$Random
at public mehritco.ir.megnatis.ejb.nlp.aiml.Aiml$Template$Random mehritco.ir.megnatis.ejb.nlp.aiml.Aiml$Template.getRandom()
at mehritco.ir.megnatis.ejb.nlp.aiml.Aiml$Template
at public mehritco.ir.megnatis.ejb.nlp.aiml.Aiml$Template mehritco.ir.megnatis.ejb.nlp.aiml.Aiml$Category.getTemplate()
at mehritco.ir.megnatis.ejb.nlp.aiml.Aiml$Category
at public java.util.List mehritco.ir.megnatis.ejb.nlp.aiml.Aiml.getCategories()
at mehritco.ir.megnatis.ejb.nlp.aiml.Aiml
at com.sun.xml.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:106)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:471)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:303)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:139)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1156)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:165)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:288)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:277)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:400)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662)
The JavaBean :
package mehritco.ir.megnatis.ejb.nlp.aiml;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "aiml")
public class Aiml {
List<Category> categories = new ArrayList<Category>();
public List<Category> getCategories() {
return categories;
}
#XmlElement
public void setCategories(List<Category> categories) {
this.categories = categories;
}
#XmlRootElement(name = "category")
public class Category<T>{
Pattern pattern ;
Template<T> template ;
public Pattern getPattern() {
return pattern;
}
#XmlElement
public void setPattern(Pattern pattern) {
this.pattern = pattern;
}
public Template getTemplate() {
return template;
}
#XmlElement
public void setTemplate(Template template) {
this.template = template;
}
}
#XmlRootElement(name = "pattern")
public class Pattern{
String pattern;
public String getPattern() {
return pattern;
}
#XmlElement
public void setPattern(String pattern) {
this.pattern = pattern;
}
}
#XmlRootElement(name = "template")
public class Template<T>{
Random random;
String template;
public Random getRandom() {
return random;
}
public void setRandom(Random random) {
this.random = random;
}
public String getTemplate() {
return template;
}
public void setTemplate(String template) {
this.template = template;
}
#XmlRootElement(name = "random")
public class Random {
List<String> li = new ArrayList<String>();
public List<String> getLi() {
return li;
}
#XmlElement(name = "li")
public void setLi(List<String> li) {
this.li = li;
}
}
}
}
The Main Method :
Aiml aiml = new Aiml();
List<Aiml.Category> listCategory = new ArrayList<Aiml.Category>();
Aiml.Category category = aiml.new Category();
Aiml.Pattern pattern = aiml.new Pattern();
pattern.setPattern("Hello");
category.setPattern(pattern);
Aiml.Template<Aiml.Template.Random> template = aiml.new Template<Aiml.Template.Random>();
Aiml.Template.Random random = template.new Random();
List<String> list = random.getLi();
list.add("hi");
list.add("hello");
random.setLi(list);
template.setRandom(random);
category.setTemplate(template);
listCategory.add(category);
aiml.setCategories(listCategory);
try {
File file = new File("D:\\TestFile\\file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(Aiml.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(aiml, file);
jaxbMarshaller.marshal(aiml, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
I need create java class that create aiml object for me to save , read the object that I'm created but in this type class Created it can't be done , some what's my problem in using JAXB?
the xml format I'm need to create is here and some other that can be happen like :
<category>
<pattern>ONCE I *</pattern>
<template>
<random>
<li>Go on.</li>
<li>Can you be more specific?</li>
<li>I did not know that.</li>
<li>Are you telling the truth?</li>
<li>I don't know what that means.</li>
<li>Try to tell me that another way.</li>
<li>What is it?</li>
</random>
</template>
</category>
For now I need to know what is the Exception here and what must to do
I need a Java data structure for some JSON data passed to me by the Datatables Editor. The format of the data received is this:
{
"action":"edit",
"data": {
"1009558":{
"weekNumber":"2"
... (more properties)
}
}
}
Here's the full documentation: https://editor.datatables.net/manual/server
Edit: The documentation shows the data sent as form params. I am stringifying the data and sending it as JSON. An example is above.
"1009558" is the row ID. If there are multiple rows sent by the editor, there would be multiple array entries (each with an ID).
Can anyone offer some advice on how to make a Java data structure for deserialization (by Spring MVC)? I can map "action" easy enough, but I'm getting stuck on the "data" element.
I'd rather suggest you to use jackson.
Here's an example, that you're asking for:
package com.github.xsavikx.jackson;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
public class JacksonTest {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
Map<String, DatabaseRow> data = new HashMap<>();
DatabaseRow row = new DatabaseRow(2, "someData");
data.put("1009558", row);
String action = "action";
DatabaseEntry dbEntry = new DatabaseEntry();
dbEntry.setAction(action);
dbEntry.setData(data);
System.out.println(objectMapper.writeValueAsString(dbEntry));
}
}
And the result:
{"action":"action","data":{"1009558":{"weekNumber":2,"someData":"someData"}}}
Models:
package com.github.xsavikx.jackson;
import java.util.Map;
public class DatabaseEntry {
private String action;
private Map<String, DatabaseRow> data;
public DatabaseEntry() {
}
public DatabaseEntry(String action, Map<String, DatabaseRow> data) {
this.action = action;
this.data = data;
}
public Map<String, DatabaseRow> getData() {
return data;
}
public void setData(Map<String, DatabaseRow> data) {
this.data = data;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
}
package com.github.xsavikx.jackson;
public class DatabaseRow {
private int weekNumber;
private String someData;
public DatabaseRow(){
}
public DatabaseRow(int weekNumber, String someData) {
this.weekNumber = weekNumber;
this.someData = someData;
}
public int getWeekNumber() {
return weekNumber;
}
public void setWeekNumber(int weekNumber) {
this.weekNumber = weekNumber;
}
public String getSomeData() {
return someData;
}
public void setSomeData(String someData) {
this.someData = someData;
}
}
Update:
more generic solution with Map of maps:
package com.github.xsavikx.jackson;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class JacksonTest {
public static void main(String[] args) throws IOException {
serializeTest();
deserializeTest();
}
private static void deserializeTest() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
DatabaseEntry databaseEntry = objectMapper.readValue("{\"action\":\"action\",\"data\":{\"1009558\":{\"weekNumber\":2,\"someData\":\"someData\"}}}", DatabaseEntry.class);
System.out.println(databaseEntry);
}
private static void serializeTest() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
Map<String,Map<String,String>> data = new HashMap<>();
Map<String,String> values = new HashMap<>();
values.put("weekDay","2");
values.put("unpredictableValue","value");
data.put("1009558", values);
String action = "action";
DatabaseEntry dbEntry = new DatabaseEntry();
dbEntry.setAction(action);
dbEntry.setData(data);
System.out.println(objectMapper.writeValueAsString(dbEntry));
}
}
Model:
package com.github.xsavikx.jackson;
import java.util.Map;
public class DatabaseEntry {
private String action;
private Map<String, Map<String,String>> data;
public DatabaseEntry() {
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public Map<String, Map<String, String>> getData() {
return data;
}
public void setData(Map<String, Map<String, String>> data) {
this.data = data;
}
}
I'm a huge fan of Joe Littlejohn's JSON tool. Provide it with a sample JSON file and it can generate POJOs for you.
Here's a sample of what it generated, based on a snipped of JSON from the site you posted.
JSON:
{
"data": [
{
"DT_RowId": "row_29",
"first_name": "Fiona",
"last_name": "Green",
"position": "Chief Operating Officer (COO)",
"office": "San Francisco",
"extn": "2947",
"salary": "850000",
"start_date": "2010-03-11"
}
]
}
JAVA:
#Generated("org.jsonschema2pojo")
public class Datum {
public String dTRowId;
public String firstName;
public String lastName;
public String position;
public String office;
public String extn;
public String salary;
public String startDate;
}
#Generated("org.jsonschema2pojo")
public class Example {
public List<Datum> data = new ArrayList<Datum>();
}
Update:
It looks like this is what the form submit actually sends:
action:edit
data[row_1][first_name]:Tiger23
data[row_1][last_name]:Nixon
data[row_1][position]:System Architect
data[row_1][office]:Edinburgh
data[row_1][extn]:5421
data[row_1][start_date]:2011-04-25
data[row_1][salary]:320800
I don't think this is Json, and I dunno if I would try to treat it as such. If you need to submit form data with Java, you might be better of using the Apache HttpComponents. You can reuse the Java "data" object above as a domain object, and then populate the POST content with Strings of the format:
data[ \DT_RowId\ ][\PropertyName\]: \PropertyValue\
With Spring Boot the json conversion between server and client is automatic (https://stackoverflow.com/a/44842806/3793165).
This way is working for me:
Controller
#PostMapping(value="/nuevoVideo")
#ResponseBody
public RespuestaCreateVideo crearNuevoVideo(#RequestBody PeticionVideos datos) {
RespuestaCreateVideo respuesta = new RespuestaCreateVideo();
respuesta.setData("datos");
//respuesta.setError("error"); // implement the logic for error and return the message to show to the user.
return respuesta;
}
where PeticionVideos (RequestVideos) is the create structure Datatables editor sends (with setters, getters...):
public class PeticionVideos {
private Map<String, Video> data;
private String action;
}
The response from the server to the client datatable is waiting for has a particular format (check at https://editor.datatables.net/manual/server).
I often use this:
public class RespuestaCreateVideo { //ResponseCreateVideo
private String data;
private String error;
}
After two days trying this is working perfect now!
Having issues reading the following XML file that I create.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<prsettings>
<prsetting>
<players>
<player>
<lastDateEntered>0</lastDateEntered>
<lastTournament>1</lastTournament>
<playerId>0</playerId>
<playersStatus>unrated</playersStatus>
<playersTag>asfd</playersTag>
<score>5.0</score>
<setsPlayed>0</setsPlayed>
<tourneysWhileInactive>0</tourneysWhileInactive>
</player>
<player>
<lastDateEntered>0</lastDateEntered>
<lastTournament>1</lastTournament>
<playerId>1</playerId>
<playersStatus>unrated</playersStatus>
<playersTag>ba</playersTag>
<score>5.0</score>
<setsPlayed>0</setsPlayed>
<tourneysWhileInactive>0</tourneysWhileInactive>
</player>
<player>
<lastDateEntered>0</lastDateEntered>
<lastTournament>1</lastTournament>
<playerId>2</playerId>
<playersStatus>unrated</playersStatus>
<playersTag>asdgf</playersTag>
<score>5.0</score>
<setsPlayed>0</setsPlayed>
<tourneysWhileInactive>0</tourneysWhileInactive>
</player>
</players>
<challongeApiKey>asbg</challongeApiKey>
<challongeUsername>asf</challongeUsername>
<implementPointDecay>false</implementPointDecay>
<numSetsNeeded>5</numSetsNeeded>
<numTourneysForActive>2</numTourneysForActive>
<numTourneysForInnactive>5</numTourneysForInnactive>
<numTourneysNeeded>5</numTourneysNeeded>
<pointsRemoved>5</pointsRemoved>
<prName>asf</prName>
<removeInnactive>false</removeInnactive>
<showPlacingDiff>false</showPlacingDiff>
<showPointDiff>false</showPointDiff>
<startOfDecay>3</startOfDecay>
</prsetting>
I have an observableList of PRSetting objects and within the PRSetting objects I have an ArrayList of Players. This is why I created a POJO file and within the PRSetting Object the only object I set up was the following.
#XmlElementWrapper(name="players")
#XmlElement(name ="player")
private ArrayList<PlayerProfile> playersList = new ArrayList<PlayerProfile>();
Here is also my POJO file that is supposed to be used to write and read the XML file.
#XmlRootElement (name = "prsettings")
public class PRSettingsWrapper {
private ObservableList<PRSettings> prList;
#XmlElement(name = "prsetting")
public ObservableList<PRSettings> getPrList(){
return prList;
}
public void setPrList(ObservableList<PRSettings> prList){
this.prList = prList;
}
}
For some reason whenever I attempt to to load the data with the following code
JAXBContext context = JAXBContext
.newInstance(PRSettingsWrapper.class);
Unmarshaller um = context.createUnmarshaller();
// Reading XML from the file and unmarshalling.
PRSettingsData wrapper = (PRSettingsData) um.unmarshal(file);
prList.clear();
prList.addAll(wrapper.getPrList());
// Save the file path to the registry.
setPrSettingsFilePath(file);
I cannot seem to successfully load the xml files into the objects. The file path is working correctly, but I'm not sure what I'm doing wrong.
Thank you in advance for your help.
Sorry to mislead you about the cause.
The problem is caused by ObservableList;
You can refer to this article:
Marshalling ObservableList with JAXB
I modified code from the link above, and the followings should be workable.
anotherTest.MyContainer
package anotherTest;
import java.util.List;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "prsettings")
public class MyContainer extends MyObject {
private ObservableList<MyObject> children = FXCollections.observableArrayList();
#XmlElements({ #XmlElement(name = "prsetting", type = MyObject.class) })
public List<MyObject> getChildren() {
return children;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("children:");
for (MyObject node : children) {
sb.append("\n");
sb.append(" " + node.toString());
}
return sb.toString();
}
}
anotherTest.MyObject
package anotherTest;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlType;
#XmlType(name = "Object")
public class MyObject {
private String challongeApiKey;
#XmlElementWrapper(name = "players")
private List<PlayerProfile> player;
public String getChallongeApiKey() {
return challongeApiKey;
}
public void setChallongeApiKey(String challongeApiKey) {
this.challongeApiKey = challongeApiKey;
}
public List<PlayerProfile> getPlayer() {
return player;
}
public void setPlayers(List<PlayerProfile> player) {
this.player = player;
}
}
anotherTest.PlayerProfile
package anotherTest;
public class PlayerProfile {
private int playerId;
private String playersStatus;
public int getPlayerId() {
return playerId;
}
public void setPlayerId(int playerId) {
this.playerId = playerId;
}
public String getPlayersStatus() {
return playersStatus;
}
public void setPlayersStatus(String playersStatus) {
this.playersStatus = playersStatus;
}
}
anotherTest.Example
package anotherTest;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Example {
public static void main(String[] args) {
// create container with list
MyContainer container = new MyContainer();
// add objects
MyObject object;
object = new MyObject();
object.setChallongeApiKey("ABCABC");
container.getChildren().add(object);
PlayerProfile p = new PlayerProfile();
p.setPlayerId(1);
p.setPlayersStatus("unrated");
List<PlayerProfile> l = new ArrayList<>();
l.add(0, p);
object.setPlayers(l);
// marshal
String baseXml = marshal(container);
// unmarshal
container = unmarshal(baseXml);
System.out.println("unmarshal test: " + container.getChildren().get(0).getChallongeApiKey());
System.out.println("unmarshal test: " + container.getChildren().get(0).getPlayer().get(0).getPlayerId());
System.out.println("unmarshal test: " + container.getChildren().get(0).getPlayer().get(0).getPlayersStatus());
System.exit(0);
}
public static String marshal(MyContainer base) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(MyContainer.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter stringWriter = new StringWriter();
jaxbMarshaller.marshal(base, stringWriter);
String xml = stringWriter.toString();
System.out.println("XML:\n" + xml);
return xml;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static MyContainer unmarshal(String xml) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(MyContainer.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
StringReader stringReader = new StringReader(xml);
MyContainer container = (MyContainer) jaxbUnmarshaller.unmarshal(stringReader);
return container;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
the output in the console is
XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<prsettings>
<prsetting>
<players>
<player>
<playerId>1</playerId>
<playersStatus>unrated</playersStatus>
</player>
</players>
<challongeApiKey>ABCABC</challongeApiKey>
</prsetting>
</prsettings>
unmarshal test: ABCABC
unmarshal test: 1
unmarshal test: unrated
Sorry, I don't know why either. I tested that if declare children to List<MyObject>, it can work properly. However, when it comes to ObservableList, you must declare it like this, or NullPointerException will occur when unmarshalling (but the same code works well on marshalling).
private ObservableList<MyObject> children = FXCollections.observableArrayList();
I use REST and i was wondering if i can tell jaxb to insert a string field "as-it-is" into the outgoing xml.
Certainly i count unpack it before returning, but i would like to save this step.
#XmlRootElement(name="unnestedResponse")
public class Response{
#Insert annotation here ;-)
private String alreadyXml;
private int otherDate; ...
}
Is there a possability to tell JAXB to just use the String as it is without escapting? I want that the client does not have to parse my response and then parse this field.
greetings,
m
You can use the #XmlAnyElement and specify a DomHandler to keep a portion of the XML document as a String.
Customer
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Customer {
private String bio;
#XmlAnyElement(BioHandler.class)
public String getBio() {
return bio;
}
public void setBio(String bio) {
this.bio = bio;
}
}
BioHandler
import java.io.*;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.annotation.DomHandler;
import javax.xml.transform.Source;
import javax.xml.transform.stream.*;
public class BioHandler implements DomHandler<String, StreamResult> {
private static final String BIO_START_TAG = "<bio>";
private static final String BIO_END_TAG = "</bio>";
private StringWriter xmlWriter = new StringWriter();
public StreamResult createUnmarshaller(ValidationEventHandler errorHandler) {
return new StreamResult(xmlWriter);
}
public String getElement(StreamResult rt) {
String xml = rt.getWriter().toString();
int beginIndex = xml.indexOf(BIO_START_TAG) + BIO_START_TAG.length();
int endIndex = xml.indexOf(BIO_END_TAG);
return xml.substring(beginIndex, endIndex);
}
public Source marshal(String n, ValidationEventHandler errorHandler) {
try {
String xml = BIO_START_TAG + n.trim() + BIO_END_TAG;
StringReader xmlReader = new StringReader(xml);
return new StreamSource(xmlReader);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
For More Information
http://blog.bdoughan.com/2011/04/xmlanyelement-and-non-dom-properties.html
Following bdoughan's answer did not work for me as I encountered errors during marshalling when the text contained the '& character (e.g. in URLs or when using HTML entities such as e.g. " ").
I was able to resolve this by changing the custom DomHandler's marshal method to
public Source marshal(String et, ValidationEventHandler veh) {
Node node = new SimpleTextNode(et);
return new DOMSource(node);
}
where SimpleTextNode implements the Node interface as follows:
class SimpleTextNode implements Node {
String nodeValue = "";
#Override
public SimpleTextNode(String nodeValue) {
this.nodeValue = nodeValue;
}
#Override
public short getNodeType() {
return TEXT_NODE;
}
// the remaining methods of the Node interface are not needed during marshalling
// you can just use the code template of your IDE...
...
}
PS: I would have loved to leave this as a comment to bdoughan's answer, but unfortunately I have way too little reputation :-(
When I run this code:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
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
#XmlAccessorType(XmlAccessType.FIELD)
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;
}
}
I get
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
<code>CA</code>
<name>Cath</name>
<salary>300</salary>
</employee>
Which is correct, so my question is why does it change the Employee to employee?
Is it possible to make it print with uppercase E, instead of employee?
This is what I actually wanted to have:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Employee>
<code>CA</code>
<name>Cath</name>
<salary>300</salary>
</Employee>
Thanks!
The behaviour you are seeing is the result of the standard JAXB (JSR-222) XML name to Java name conversion algorithm.
You can use the #XmlRootElement annotation to specify a name:
#XmlRootElement(name="Employee")
#XmlAccessorType(XmlAccessType.FIELD)
class Employee {
...
}
I'm the EclipseLink JAXB (MOXy) lead, and we have an extension that allows you to override the default name conversion algorithm that you may be interested in:
http://blog.bdoughan.com/2011/05/overriding-jaxbs-name-mangling.html
For specific elements...
#XmlElement( name = "Code")
private String code;
For the object....
#XmlRootElement(name="Employee")
public class Employee{ ...
My solution after put #XmlElement(name="Xxxxx") to fields and used XStream.aliasField(). This is more generic because it uses annotations and scans other class calls in the same package.
import java.lang.reflect.Field;
import java.util.Map;
import java.util.TreeMap;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlAttribute;
import com.thoughtworks.xstream.XStream;
import my.MyClassGeneratedFromXsdToJaxB;
public class TestChangeFirstLetterXml {
public static void main(String[] args) throws ClassNotFoundException {
MyClassGeneratedFromXsdToJaxB myClassGeneratedFromXsdToJaxB=new MyClassGeneratedFromXsdToJaxB();
XStream xstream = new XStream();
xstream.autodetectAnnotations(true);
xstream = makeAliasAnnotatedFields(xstream, MyClassGeneratedFromXsdToJaxB.class, "FirstTagOrRoot");
//System.out.println(xstream.toXML(myClassGeneratedFromXsdToJaxB));
}
public static XStream makeAliasAnnotatedFields(XStream xstream, Class myclass, String firstTag)
throws ClassNotFoundException {
xstream.alias(firstTag, myclass);
Map<String, Object[]> aliaslist = getListAlias(myclass);
for (String key : aliaslist.keySet()) {
Object[] aliasvalue = new Object[3];
aliasvalue = aliaslist.get(key);
String xmlTag = new String((String) aliasvalue[0]);
Class<?> classJaxb = (Class<?>) aliasvalue[1];
String tagToRename = new String((String) aliasvalue[2]);
xstream.aliasField(xmlTag, classJaxb, tagToRename);
System.out.println("AliasField " + xmlTag + " " + classJaxb.getName() + " " + tagToRename);
}
return xstream;
}
public static Map<String, Object[]> getListAlias(Class<?> classToCheck)
throws ClassNotFoundException {
/* Read recursive fields of the class */
Field[] fs = classToCheck.getDeclaredFields();
String annotationsPackage = classToCheck.getPackage().getName();
String classSimpleName = new String(classToCheck.getSimpleName());
/* it is necessary avoid loop */
Map<String, Object[]> aliasStart = new TreeMap<String, Object[]>();
/* */
for (int i = 0; i < fs.length; i++) {
String nameField = fs[i].getName();
String classFieldName = new String(fs[i].getType().getName());
String nameXmlXsd = new String("");
String idkey = new String(annotationsPackage + ".");
if (fs[i].isAnnotationPresent(javax.xml.bind.annotation.XmlElement.class)) {
XmlElement atrib = fs[i].getAnnotation(XmlElement.class);
nameXmlXsd = new String(atrib.name());
idkey = new String(idkey + classSimpleName + ".Element." + nameField);
} else if (fs[i].isAnnotationPresent(javax.xml.bind.annotation.XmlAttribute.class)) {
XmlAttribute atrib = fs[i].getAnnotation(XmlAttribute.class);
nameXmlXsd = new String(atrib.name());
idkey = new String(idkey + classSimpleName + ".Type." + nameField);
}
if (aliasStart.containsKey(idkey)) /* avoid loop */
continue;
if (nameXmlXsd.equals("Signature")) // My particular condition
continue;
if (!nameXmlXsd.equals(classFieldName)) {
// xstrem.aliasField(a,b,c)
Object[] alias = new Object[3];
alias[0] = new String(nameXmlXsd);
alias[1] = classToCheck;
alias[2] = new String(nameField);
aliasStart.put(idkey, alias);
}
if (classFieldName.indexOf(annotationsPackage) > -1) {
Class<?> c = Class.forName(classFieldName);
Map<String, Object[]> aliaslist = getListAlias(c);
for (String key : aliaslist.keySet()) {
Object[] aliasvalue = new Object[3];
aliasvalue = aliaslist.get(key);
aliasStart.put(key, aliasvalue);
}
}
}
return aliasStart;
}
}
An alternative answer, if JAXB is not a MUST, then you can actually use org.json jar to convert the object to a JSONObject, then from there, you can use the XML object to convert the JSONObject to an XML. You will need a few tweaks before it can be a standalone XML.
A code snippet example:
public static String getXMLString(Object o){
JSONObject json = new JSONObject(o);
String result =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
XML.toString(json, o.getClass().getSimpleName());
return result;
}