JAXB + Enums + Showing Multiple Values - java

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.

Related

Parse an xml file of the below format in Java

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'

Cannot read XML file using JAXB?

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();

JAXB - Move dynamically generated namespaces to document root

I've got this POJO, encapsulating a dynamic, non-nested element of an Atom entry:
public class SimpleElement {
private Namespace namespace;
private String tagName;
private String value;
private Collection<Attribute> attributes;
/* getters/setters/... */
And for completeness, Attribute
public class Attribute {
private String name;
private String value;
private Namespace namespace;
/* getters/setters/... */
And Namespace:
public class Namespace {
private final String uri;
private final String prefix;
/* getters/setters/... */
SimpleElementAdapter serializes a SimpleElement into its org.w3c.dom.Element counterpart.
The only problem with this approach is that namespaces always end up at element level, never at document root.
Is there a way to dynamically declare namespaces at document root?
MY RECOMMENDATION
My recommendation is to let the JAXB implementation write the namespace declarations as it sees fit. As long as the elements are properly namespace qualified it does not really matter where the namespace declarations occur.
If you ignore my recommendation, below is an approach you can use.
ORIGINAL ANSWER
Specify the Namespaces to Include on Root Element
You can use the NamespacePrefixMapper extension to add extra namespace declarations to the root element (see: https://jaxb.java.net/nonav/2.2.11/docs/ch05.html#prefixmapper). You will need to derive from your own object model what namespaces should be declared at the root.
Note: NamespacePrefixMapper is in the com.sun.xml.bind.marshaller package. This means you will need the JAXB refereince implementation jar on your classpath (see: https://jaxb.java.net/).
import com.sun.xml.bind.marshaller.*;
public class MyNamespacePrefixMapper extends NamespacePrefixMapper {
#Override
public String getPreferredPrefix(String arg0, String arg1, boolean arg2) {
return null;
}
#Override
public String[] getPreDeclaredNamespaceUris2() {
return new String[] {"ns1", "http://www.example.com/FOO", "ns2", "http://www.example.com/BAR"};
}
}
Specify the NamespacePrefixMapper on the Marshaller
The com.sun.xml.bind.namespacePrefixMapper property is used to specify the NamespacePrefixMapper on the Marshaller.
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper());
Demo Code
Java Model (Foo)
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Foo {
private Object object;
#XmlAnyElement
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}
Demo
import javax.xml.bind.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.w3c.dom.Element;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Foo foo = new Foo();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.newDocument();
Element element = document.createElementNS("http://www.example.com/FOO", "ns1:foo");
foo.setObject(element);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper());
marshaller.marshal(foo, System.out);
}
}
Output
Below is sample output that will be produced:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo xmlns:ns1="http://www.example.com/FOO" xmlns:ns2="http://www.example.com/BAR">
<ns1:foo/>
</foo>
UPDATE
Clear answer, thanks. However, I need access to the NSMapper from
SimpleElementAdapter. What do you suggest? The only way I see right
now is making the NSMapper a mutable singleton so that
SimpleElementAdapter can add namespaces if needed.
I forgot about your XmlAdapter.
Java Model
Below is a more complicated iteration of the model, where instead of Foo holding an instance of a DOM element, it holds and instance of Bar that gets adapted into an instance of a DOM element.
Foo
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class Foo {
private Bar bar;
#XmlAnyElement
#XmlJavaTypeAdapter(BarAdapter.class)
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
}
Bar
public class Bar {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
BarAdapter
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.*;
import org.w3c.dom.*;
public class BarAdapter extends XmlAdapter<Object, Bar>{
#Override
public Object marshal(Bar bar) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.newDocument();
Element element = document.createElementNS("http://www.example.com/BAR", "ns:bar");
element.setTextContent(bar.getValue());
return element;
}
#Override
public Bar unmarshal(Object arg0) throws Exception {
// TODO Auto-generated method stub
return null;
}
}
Grab Namespace Declarations
Since your object model does not hold the DOM elements directly you can't traverse it to get the namespace declarations. Instead we could do a marshal to a ContentHandler to collect them. Below are the reasons for marshalling to a ContentHandler:
It gives us an easy event which we can use to collection the namespace declarations.
It doesn't actually produce anything so it is the lightest marshal target we can use.
NsContentHandler contentHandler = new NsContentHandler();
marshaller.marshal(foo, contentHandler);
NsContentHandler
The implementation of ContentHandler will look something like:
import java.util.*;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class NsContentHandler extends DefaultHandler {
private Map<String, String> namespaces = new TreeMap<String, String>();
#Override
public void startPrefixMapping(String prefix, String uri) throws SAXException {
if(!namespaces.containsKey(prefix)) {
namespaces.put(prefix, uri);
}
}
public Map<String, String> getNamespaces() {
return namespaces;
}
}
Specify the Namespaces to Include on Root Element
The implementation of MyNamespacePrefixMapper changes a little to use the namrespaces captured from our ContentHandler.
import java.util.Map;
import java.util.Map.Entry;
import com.sun.xml.bind.marshaller.*;
public class MyNamespacePrefixMapper extends NamespacePrefixMapper {
private String[] namespaces;
public MyNamespacePrefixMapper(Map<String, String> namespaces) {
this.namespaces = new String[namespaces.size() * 2];
int index = 0;
for(Entry<String, String> entry : namespaces.entrySet()) {
this.namespaces[index++] = entry.getKey();
this.namespaces[index++] = entry.getValue();
}
}
#Override
public String getPreferredPrefix(String arg0, String arg1, boolean arg2) {
return null;
}
#Override
public String[] getPreDeclaredNamespaceUris2() {
return namespaces;
}
}
Demo Code
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Bar bar = new Bar();
bar.setValue("Hello World");
Foo foo = new Foo();
foo.setBar(bar);
Marshaller marshaller = jc.createMarshaller();
// Marshal First Time to Get Namespace Declarations
NsContentHandler contentHandler = new NsContentHandler();
marshaller.marshal(foo, contentHandler);
// Marshal Second Time for Real
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper(contentHandler.getNamespaces()));
marshaller.marshal(foo, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo xmlns:ns="http://www.example.com/BAR">
<ns:bar>Hello World</ns:bar>
</foo>

JAXB. Get boolean from string

In my XML I have
<myelem required="false"/>
How I can read the required attribute as a boolean? I can read it as String and inside a getter do this: return new Boolean(required)
But maybe there are some more elegant ways?
Just simply use boolean for the member in your Java class:
#XmlAttribute
private boolean required;
Or, if you use getter-setter style of mapping:
#XmlAttribute
public boolean isRequired() {
return required;
}
The JAXB unmarshaller is able to interpret "true" and "false" strings in the XML document as boolean value.
UPDATE:
I tested this with the following classes:
test/MyElem.java:
package test;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="myelem")
public class MyElem {
private boolean required;
#XmlAttribute
public boolean isRequired() {
return required;
}
public void setRequired(boolean value) {
required = value;
}
}
Test.java:
import javax.xml.bind.*;
import java.io.*;
import test.*;
public class Test {
public static void main(String[] args) {
try {
JAXBContext jc = JAXBContext.newInstance(MyElem.class);
Unmarshaller u = jc.createUnmarshaller();
Object o = u.unmarshal( new File( "test.xml" ) );
System.out.println(((MyElem)o).isRequired());
} catch(Exception e) {
e.printStackTrace();
}
}
}
And with the following input (test.xml):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myelem required="true"/>
I get the correct result on the console:
true

JAXB Java generating XML, Why lowercase?

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;
}

Categories

Resources