I have an xml file in the below format that i get as a response from a service. It isn't of the traditional xml format where values are enclosed within respective tags. It's just a sample while the actual file will have hundreds of elements. How do i get to the node which I require (lets say 'TDE-2') in the most efficient manner and put its value in a map like {map(TenderID, TDE-2), map(ContactID, null)}
<xml version="1.0" encoding="UTF-8"?>
<report>
<report_header>
<c1>TenderID</c1>
<c2>ContactID</c2>
<c3>Address</c3>
<c4>Description</c4>
<c5>Date</c5>
</report_header>
<report_row>
<c1>TDE-1</c1>
<c2></c2>
<c3></c3>
<c4>Tender 1</c4>
<c5>09/30/2016</c5>
</report_row>
<report_row>
<c1>TDE-2</c1>
<c2></c2>
<c3></c3>
<c4>Tender 2</c4>
<c5>10/02/2016</c5>
</report_row>
</report>
JAXB allows you to deserialise XML into Java Objects. If you create Java POJOs to match the XML document model, you can then use JAXB to unmarshal the XML in the POJO.
for example:
POJOs:
Report.java:
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Report {
private List<ReportRow> reportRows;
public List<ReportRow> getReportRows() {
return reportRows;
}
#XmlElement(name = "report_row")
public void setReportRows(List<ReportRow> reportRows) {
this.reportRows = reportRows;
}
}
ReportRow.java
import javax.xml.bind.annotation.XmlElement;
public class ReportRow {
private String c1;
private String c2;
private String c3;
private String c4;
public String getC1() {
return c1;
}
#XmlElement
public void setC1(String c1) {
this.c1 = c1;
}
public String getC2() {
return c2;
}
#XmlElement
public void setC2(String c2) {
this.c2 = c2;
}
public String getC3() {
return c3;
}
#XmlElement
public void setC3(String c3) {
this.c3 = c3;
}
public String getC4() {
return c4;
}
#XmlElement
public void setC4(String c4) {
this.c4 = c4;
}
}
Code to read your XML and bind it into java objects:
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import org.junit.Test;
public class JaxbTest {
#Test
public void testFoo() throws JAXBException {
File xmlFile = new File("src/test/resources/reports.xml");
JAXBContext context = JAXBContext.newInstance(Report.class, ReportRow.class);
Unmarshaller jaxbUnmarshaller = context.createUnmarshaller();
Report report = (Report) jaxbUnmarshaller.unmarshal(xmlFile);
ReportRow reportYouWant = report.getReportRows().stream().filter(reportRow -> reportRow.getC1().equals("TDE-1"))
.findFirst().get();
}
}
You also need to add the following dependencies to your build script:
compile group: 'javax.xml', name: 'jaxb-impl', version: '2.1'
compile group: 'javax.xml', name: 'jaxb-api', version: '2.1'
Related
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 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");
}
}
By default Gson uses fields as a basis for it's serialization. Is there a way to get it to use accessors instead?
The developers of Gson say that they never felt swayed by the requests to add this feature and they were worried about murkying up the api to add support for this.
One way of adding this functionality is by using a TypeAdapter (I apologize for the gnarly code but this demonstrates the principle):
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.google.common.base.CaseFormat;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
public class AccessorBasedTypeAdaptor<T> extends TypeAdapter<T> {
private Gson gson;
public AccessorBasedTypeAdaptor(Gson gson) {
this.gson = gson;
}
#SuppressWarnings("unchecked")
#Override
public void write(JsonWriter out, T value) throws IOException {
out.beginObject();
for (Method method : value.getClass().getMethods()) {
boolean nonBooleanAccessor = method.getName().startsWith("get");
boolean booleanAccessor = method.getName().startsWith("is");
if ((nonBooleanAccessor || booleanAccessor) && !method.getName().equals("getClass") && method.getParameterTypes().length == 0) {
try {
String name = method.getName().substring(nonBooleanAccessor ? 3 : 2);
name = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, name);
Object returnValue = method.invoke(value);
if(returnValue != null) {
TypeToken<?> token = TypeToken.get(returnValue.getClass());
TypeAdapter adapter = gson.getAdapter(token);
out.name(name);
adapter.write(out, returnValue);
}
} catch (Exception e) {
throw new ConfigurationException("problem writing json: ", e);
}
}
}
out.endObject();
}
#Override
public T read(JsonReader in) throws IOException {
throw new UnsupportedOperationException("Only supports writes.");
}
}
You can register this as a normal type adapter for a given type or through a TypeAdapterfactory - possibly checking for the presence of a runtime annotation:
public class TypeFactory implements TypeAdapterFactory {
#SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
Class<? super T> t = type.getRawType();
if(t.isAnnotationPresent(UseAccessor.class)) {
return (TypeAdapter<T>) new AccessorBasedTypeAdaptor(gson);
}
return null;
}
This can be specified as normal when creating your gson instance:
new GsonBuilder().registerTypeAdapterFactory(new TypeFactory()).create();
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
If you can't get Gson to do what you want, below is how you can accomplish this using MOXy's native JSON binding. MOXy like any JAXB implementation will use property (public) access by default. You can configure field access using #XmlAccessorType(XmlAccessType.FIELD). Below is an example:
Customer
package forum11385214;
public class Customer {
private String foo;
private Address bar;
public String getName() {
return foo;
}
public void setName(String name) {
this.foo = name;
}
public Address getAddress() {
return bar;
}
public void setAddress(Address address) {
this.bar = address;
}
}
Address
package forum11385214;
public class Address {
private String foo;
public String getStreet() {
return foo;
}
public void setStreet(String street) {
this.foo = street;
}
}
jaxb.properties
To configure MOXy as your JAXB provider you need to add a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
package forum11385214;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(2);
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource json = new StreamSource("src/forum11385214/input.json");
Customer customer = (Customer) unmarshaller.unmarshal(json, Customer.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
}
}
input.json/Output
{
"name" : "Jane Doe",
"address" : {
"street" : "1 Any Street"
}
}
For More Information
http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
http://blog.bdoughan.com/2012/04/jaxb-and-unmapped-properties.html
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;
}
I have an enum:
#XmlEnum
#XmlRootElement
public enum Product {
POKER("favourite-product-poker"),
SPORTSBOOK("favourite-product-casino"),
CASINO("favourite-product-sportsbook"),
SKILL_GAMES("favourite-product-skill-games");
private static final String COULD_NOT_FIND_PRODUCT = "Could not find product: ";
private String key;
private Product(final String key) {
this.key = key;
}
/**
* #return the key
*/
public String getKey() {
return key;
}
that I output in a REST service like so:
GenericEntity<List<Product>> genericEntity = new GenericEntity<List<Product>>(products) {
};
return Response.ok().entity(genericEntity).build();
and it outputs like this:
<products>
<product>POKER</product>
<product>SPORTSBOOK</product>
<product>CASINO</product>
<product>SKILL_GAMES</product>
</products>
I want it to output with both the enum name (i.e, POKER) and the key (i.e, "favourite-product-poker").
I have tried a number of different ways of doing this including using #XmlElement, #XmlEnumValue and #XmlJavaTypeAdapter, without getting both out at the same time.
Does anyone know how to achieve this, as you would for a normal JAXB annotated bean?
Thanks.
You could create a wrapper object for this, something like:
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
#XmlRootElement(name="product")
public class ProductWrapper {
private Product product;
#XmlValue
public Product getValue() {
return product;
}
public void setValue(Product value) {
this.product = value;
}
#XmlAttribute
public String getKey() {
return product.getKey();
}
}
This would correspond to the following XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<product key="favourite-product-poker">POKER</product>
You would need to pass instances of ProductWrapper to JAXB instead of Product.
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ProductWrapper.class);
ProductWrapper pw = new ProductWrapper();
pw.setValue(Product.POKER);
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(pw, System.out);
}
}
You can use an adapter:
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.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
public class XmlEnumTest{
public static void main(String...str) throws Exception{
JAXBContext jc = JAXBContext.newInstance(ProductList.class);
StringWriter sw = new StringWriter();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(new ProductList(),sw);
System.out.println(sw.toString());
}
}
class ProductTypeAdaper extends XmlAdapter<ProductAdapter, Product> {
#Override
public Product unmarshal(ProductAdapter v) throws Exception {
return Product.valueOf(v.value);
}
#Override
public ProductAdapter marshal(Product v) throws Exception {
ProductAdapter result = new ProductAdapter();
result.key = v.getKey();
result.value = v.name();
return result;
}
}
#XmlType
class ProductAdapter{
#XmlAttribute
public String key;
#XmlValue
public String value;
}
#XmlJavaTypeAdapter(ProductTypeAdaper.class)
enum Product{
POKER("favourite-product-poker"),
SPORTSBOOK("favourite-product-casino"),
CASINO("favourite-product-sportsbook"),
SKILL_GAMES("favourite-product-skill-games");
private static final String COULD_NOT_FIND_PRODUCT = "Could not find product: ";
private String key;
private Product(final String key) {
this.key = key;
}
/**
* #return the key
*/
public String getKey() {
return key;
}
}
#XmlRootElement
#XmlSeeAlso({Product.class})
class ProductList{
#XmlElementWrapper(name="products")
#XmlElement(name="product")
private List<Product> list = new ArrayList<Product>(){{add(Product.POKER);add(Product.SPORTSBOOK);add(Product.CASINO);}};
}
You need to remove the #XmlEnum from your enum value, if you want it to be serialized into XML like a normal object. An enum (by definition) is represented in the XML by a single string symbol. This allows combining it with #XmlList, for example, to create an efficient, whitespace-separated list of items.