Issue using JAXB, my POJO's fields are not being Mapped - java

I am using Java 8 Springboot with Maven.
When I try to unmarshall the XML and map the FTMessage object with the given parameters, the object is created, but all the fields of the object are mapped
to null.
When wrapping the unmarshaller in a try-catch, JAXB runs and no errors are thrown.
So the resulting is:
userShortName = null and filename = null
Here is my code:
JAXBContext jaxbContext = JAXBContext.newInstance( FTMessage.class );
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(xmlMessage); //xmlMessage is a string.
FTMessage msg = ((FTMessage) unmarshaller.unmarshal(reader)); //msg ends up with fields populated with null.
And my Object Class:
package com.project.message;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "FTMessage", namespace = "https://www.example.com/exmpl" )
public class FTMessage /*implements Serializable*/ {
#XmlElement(name = "UserShortName")
private String userShortName;
#XmlElement(name = "Filename")
private String filename;
public FTMessage () {
}
public FTMessage (String userShortName, String filename) {
this.userShortName= userShortName;
this.fundShortName = filename;
}
#Override
public String toString() {
return "FTMessage {userShortName: " + this.userShortName+ ", " +
"filename : " + this.filename + "}";
}
public String getUserShortName() {
return userShortName;
}
public void setUserShortName(String userShortName) {
this.userShortName = userShortName;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
}
as well as my XML String:
<?xml version="1.0" encoding="UTF-8"?>
<FTMessage xmlns="https://www.example.com/exmpl"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<UserShortName>Jerome</UserShortName>
<Filename>NotReal.csv</Filename>
</FTMessage>
If I marshal it I get a file with the following:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:FTMessage xmlns:ns2="https://www.example.com/exmpl">
<UserShortName>Jerome</UserShortName>
<Filename>notReal</Filename>
</ns2:FTMessage>

The salution which I found was adding the namespace attribute-not only to the #XmlRootElement but also to all the #XmlElement's like so:
#XmlElement(name = "UserShortName", namespace = "https://www.example.com/exmpl")

Related

XML JAXB marshall not saving to xml file

I have a javafx program that brings up a filechooser to allow a user to pick and image an display it to a grid view with an inserted caption that pops up after an image was pciked. I save both the filepath and the caption to different arraylists [for now] and my goal is to save both to xml file so that I can unmarshall it when the application is re opened so the images would still be there. Right now I just want to be able to save the two strings to an xml file and then figure out the rest later. I am currently able to run my code with no errors until I reach my stop method where I try to save every image and caption the user has added to the array lists.
My JAXB Annotation:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ImageCap {
private String filePath;
private String caption;
public ImageCap() {
}
public ImageCap(String filePath, String caption) {
this.filePath = filePath;
this.caption = caption;
}
#Override
public String toString() {
return "ImageCap{" + "filePath=" + filePath + ", caption=" + caption + '}';
}
public String getFilePath() {
return filePath;
}
#XmlElement
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public String getCaptions() {
return caption;
}
#XmlElement
public void setCaption(String caption) {
this.caption = caption;
}
}
And my main to test:
public static void main(String[] args) {launch(args);}
public void start(Stage primaryStage) throws JAXBException{
final JFXPanel bananarama = new JFXPanel();
//import the library (read))
// create the (initial) display
display.makeBrowseButton(primaryStage);
display.createDisplay(primaryStage);
// show user
primaryStage.show();
}#Override
public void stop() throws JAXBException{
File file = new File("file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(ImageCap.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
//this.context = JAXBContext.newInstance(ImageCap.class);
//Marshaller marshaller = context.createMarshaller();
for(int i = 0; i < display.filePaths.size(); i++)
{
ImageCap imageCap = new ImageCap();
imageCap.setFilePath(display.filePaths.get(i));
imageCap.setCaption(display.captions.get(i).toString());
System.out.println(display.filePaths.get(i).toString());
try {
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(imageCap, file);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Here are my errors after the stop command:
this problem is related to the following location:
at public void ImageCap.setCaption(java.lang.String)
at ImageCap
this problem is related to the following location:
at private java.lang.String ImageCap.caption
at ImageCap
Class has two properties of the same name "filePath"
this problem is related to the following location:
at public java.lang.String ImageCap.getFilePath()
at ImageCap
this problem is related to the following location:
at private java.lang.String ImageCap.filePath
at ImageCap
but it specically cuts off at line 81 which is:
JAXBContext jaxbContext = JAXBContext.newInstance(ImageCap.class);
any ideas why?
If you have getter and setters for a field of the same name, then you need to use XmlAccessType.PROPERTY rather than XmlAccessType.FIELD:
#XmlRootElement
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class ImageCap {
private String filePath;
private String caption;
...
Also, another problem that you will encounter is that you misspelled getCaptions() as plural, when it should be getCaption(). JAXB will complain about it as well.
Finally, by marshaling your file inside the loop, you are rewriting over and over the same file with the currently processed imageCap. If what you want is to marshall all imageCaps you need to put them on a List and marshall the List instead. In order to do that you'd need a new JAXB model class like:
#XmlRootElement(name = "myImageCapList")
class ImageCapList {
#XmlElement
List<ImageCap> imageCap;
public ImageCapList() {}
public ImageCapList(List<ImageCap> imageCaps) {
this.imageCap = imageCaps;
}
}
and you'd need to create an instance of this object wrapping your list of ImageCap objects (List<ImageCap>) and use it as the target to invoke the jaxbMarshaller.marshal method as shown in the following method:
public void imageCapsMarshal(List<ImageCap> imageCaps, File outFile) {
try {
jaxbMarshaller.marshal(new ImageCapList(imageCaps), outFile);
} catch (JAXBException e) {
// HANDLE EXCEPTIONS
}
}
also, you'll need to instantiate your JAXBContext appropriately:
jaxbContext = JAXBContext.newInstance(ImageCapList.class);
The following is a complete working demo of this for you to play with:
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
public class JAXBMarshall {
private JAXBContext jaxbContext;
private Marshaller jaxbMarshaller;
public JAXBMarshall() throws JAXBException {
jaxbContext = JAXBContext.newInstance(ImageCapList.class);
jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
}
public void imageCapsMarshal(List<ImageCap> imageCaps, File outFile) {
try {
jaxbMarshaller.marshal(new ImageCapList(imageCaps), outFile);
} catch (JAXBException e) {
// HANDLE EXCEPTIONS
}
}
public static void main(String[] args) throws JAXBException {
JAXBMarshall jaxbMarshaller = new JAXBMarshall();
File file = new File("file.xml");
List<ImageCap> imageCaps = IntStream.range(0, 10)
.mapToObj(idx -> new ImageCap("my/file/path/" + idx, idx + ". The Caption!"))
.collect(Collectors.toList());
jaxbMarshaller.imageCapsMarshal(imageCaps, file);
}
#XmlRootElement(name = "myImageCapList")
static class ImageCapList {
#XmlElement
List<ImageCap> imageCap;
public ImageCapList() {}
public ImageCapList(List<ImageCap> imageCaps) {
this.imageCap = imageCaps;
}
}
#XmlRootElement
static class ImageCap {
#XmlElement
String filePath;
#XmlElement
String caption;
public ImageCap() {}
public ImageCap(String filePath, String caption) {
this.filePath = filePath;
this.caption = caption;
}
#Override
public String toString() {
return "ImageCap{" + "filePath=" + filePath + ", caption=" + caption + '}';
}
}
}
Complete code on GitHub
Hope this helps.

Create xml Elements and sub elements in Java using dom [duplicate]

I have to read and write to and from an XML file. What is the easiest way to read and write XML files using Java?
Here is a quick DOM example that shows how to read and write a simple xml file with its dtd:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE roles SYSTEM "roles.dtd">
<roles>
<role1>User</role1>
<role2>Author</role2>
<role3>Admin</role3>
<role4/>
</roles>
and the dtd:
<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT roles (role1,role2,role3,role4)>
<!ELEMENT role1 (#PCDATA)>
<!ELEMENT role2 (#PCDATA)>
<!ELEMENT role3 (#PCDATA)>
<!ELEMENT role4 (#PCDATA)>
First import these:
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.xml.sax.*;
import org.w3c.dom.*;
Here are a few variables you will need:
private String role1 = null;
private String role2 = null;
private String role3 = null;
private String role4 = null;
private ArrayList<String> rolev;
Here is a reader (String xml is the name of your xml file):
public boolean readXML(String xml) {
rolev = new ArrayList<String>();
Document dom;
// Make an instance of the DocumentBuilderFactory
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
// use the factory to take an instance of the document builder
DocumentBuilder db = dbf.newDocumentBuilder();
// parse using the builder to get the DOM mapping of the
// XML file
dom = db.parse(xml);
Element doc = dom.getDocumentElement();
role1 = getTextValue(role1, doc, "role1");
if (role1 != null) {
if (!role1.isEmpty())
rolev.add(role1);
}
role2 = getTextValue(role2, doc, "role2");
if (role2 != null) {
if (!role2.isEmpty())
rolev.add(role2);
}
role3 = getTextValue(role3, doc, "role3");
if (role3 != null) {
if (!role3.isEmpty())
rolev.add(role3);
}
role4 = getTextValue(role4, doc, "role4");
if ( role4 != null) {
if (!role4.isEmpty())
rolev.add(role4);
}
return true;
} catch (ParserConfigurationException pce) {
System.out.println(pce.getMessage());
} catch (SAXException se) {
System.out.println(se.getMessage());
} catch (IOException ioe) {
System.err.println(ioe.getMessage());
}
return false;
}
And here a writer:
public void saveToXML(String xml) {
Document dom;
Element e = null;
// instance of a DocumentBuilderFactory
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
// use factory to get an instance of document builder
DocumentBuilder db = dbf.newDocumentBuilder();
// create instance of DOM
dom = db.newDocument();
// create the root element
Element rootEle = dom.createElement("roles");
// create data elements and place them under root
e = dom.createElement("role1");
e.appendChild(dom.createTextNode(role1));
rootEle.appendChild(e);
e = dom.createElement("role2");
e.appendChild(dom.createTextNode(role2));
rootEle.appendChild(e);
e = dom.createElement("role3");
e.appendChild(dom.createTextNode(role3));
rootEle.appendChild(e);
e = dom.createElement("role4");
e.appendChild(dom.createTextNode(role4));
rootEle.appendChild(e);
dom.appendChild(rootEle);
try {
Transformer tr = TransformerFactory.newInstance().newTransformer();
tr.setOutputProperty(OutputKeys.INDENT, "yes");
tr.setOutputProperty(OutputKeys.METHOD, "xml");
tr.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
tr.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "roles.dtd");
tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
// send DOM to file
tr.transform(new DOMSource(dom),
new StreamResult(new FileOutputStream(xml)));
} catch (TransformerException te) {
System.out.println(te.getMessage());
} catch (IOException ioe) {
System.out.println(ioe.getMessage());
}
} catch (ParserConfigurationException pce) {
System.out.println("UsersXML: Error trying to instantiate DocumentBuilder " + pce);
}
}
getTextValue is here:
private String getTextValue(String def, Element doc, String tag) {
String value = def;
NodeList nl;
nl = doc.getElementsByTagName(tag);
if (nl.getLength() > 0 && nl.item(0).hasChildNodes()) {
value = nl.item(0).getFirstChild().getNodeValue();
}
return value;
}
Add a few accessors and mutators and you are done!
Writing XML using JAXB (Java Architecture for XML Binding):
http://www.mkyong.com/java/jaxb-hello-world-example/
package com.mkyong.core;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Customer {
String name;
int age;
int id;
public String getName() {
return name;
}
#XmlElement
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
#XmlElement
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
#XmlAttribute
public void setId(int id) {
this.id = id;
}
}
package com.mkyong.core;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
public class JAXBExample {
public static void main(String[] args) {
Customer customer = new Customer();
customer.setId(100);
customer.setName("mkyong");
customer.setAge(29);
try {
File file = new File("C:\\file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(customer, file);
jaxbMarshaller.marshal(customer, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
The above answer only deal with DOM parser (that normally reads the entire file in memory and parse it, what for a big file is a problem), you could use a SAX parser that uses less memory and is faster (anyway that depends on your code).
SAX parser callback some functions when it find a start of element, end of element, attribute, text between elements, etc, so it can parse the document and at the same time you
get what you need.
Some example code:
http://www.mkyong.com/java/how-to-read-xml-file-in-java-sax-parser/
The answers only cover DOM / SAX and a copy paste implementation of a JAXB example.
However, one big area of when you are using XML is missing. In many projects / programs there is a need to store / retrieve some basic data structures. Your program has already a classes for your nice and shiny business objects / data structures, you just want a comfortable way to convert this data to a XML structure so you can do more magic on it (store, load, send, manipulate with XSLT).
This is where XStream shines. You simply annotate the classes holding your data, or if you do not want to change those classes, you configure a XStream instance for marshalling (objects -> xml) or unmarshalling (xml -> objects).
Internally XStream uses reflection, the readObject and readResolve methods of standard Java object serialization.
You get a good and speedy tutorial here:
To give a short overview of how it works, I also provide some sample code which marshalls and unmarshalls a data structure.
The marshalling / unmarshalling happens all in the main method, the rest is just code to generate some test objects and populate some data to them.
It is super simple to configure the xStream instance and marshalling / unmarshalling is done with one line of code each.
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import com.thoughtworks.xstream.XStream;
public class XStreamIsGreat {
public static void main(String[] args) {
XStream xStream = new XStream();
xStream.alias("good", Good.class);
xStream.alias("pRoDuCeR", Producer.class);
xStream.alias("customer", Customer.class);
Producer a = new Producer("Apple");
Producer s = new Producer("Samsung");
Customer c = new Customer("Someone").add(new Good("S4", 10, new BigDecimal(600), s))
.add(new Good("S4 mini", 5, new BigDecimal(450), s)).add(new Good("I5S", 3, new BigDecimal(875), a));
String xml = xStream.toXML(c); // objects -> xml
System.out.println("Marshalled:\n" + xml);
Customer unmarshalledCustomer = (Customer)xStream.fromXML(xml); // xml -> objects
}
static class Good {
Producer producer;
String name;
int quantity;
BigDecimal price;
Good(String name, int quantity, BigDecimal price, Producer p) {
this.producer = p;
this.name = name;
this.quantity = quantity;
this.price = price;
}
}
static class Producer {
String name;
public Producer(String name) {
this.name = name;
}
}
static class Customer {
String name;
public Customer(String name) {
this.name = name;
}
List<Good> stock = new ArrayList<Good>();
Customer add(Good g) {
stock.add(g);
return this;
}
}
}
Ok, already having DOM, JaxB and XStream in the list of answers, there is still a complete different way to read and write XML: Data projection You can decouple the XML structure and the Java structure by using a library that provides read and writeable views to the XML Data as Java interfaces. From the tutorials:
Given some real world XML:
<weatherdata>
<weather
...
degreetype="F"
lat="50.5520210266113" lon="6.24060010910034"
searchlocation="Monschau, Stadt Aachen, NW, Germany"
... >
<current ... skytext="Clear" temperature="46"/>
</weather>
</weatherdata>
With data projection you can define a projection interface:
public interface WeatherData {
#XBRead("/weatherdata/weather/#searchlocation")
String getLocation();
#XBRead("/weatherdata/weather/current/#temperature")
int getTemperature();
#XBRead("/weatherdata/weather/#degreetype")
String getDegreeType();
#XBRead("/weatherdata/weather/current/#skytext")
String getSkytext();
/**
* This would be our "sub projection". A structure grouping two attribute
* values in one object.
*/
interface Coordinates {
#XBRead("#lon")
double getLongitude();
#XBRead("#lat")
double getLatitude();
}
#XBRead("/weatherdata/weather")
Coordinates getCoordinates();
}
And use instances of this interface just like POJOs:
private void printWeatherData(String location) throws IOException {
final String BaseURL = "http://weather.service.msn.com/find.aspx?outputview=search&weasearchstr=";
// We let the projector fetch the data for us
WeatherData weatherData = new XBProjector().io().url(BaseURL + location).read(WeatherData.class);
// Print some values
System.out.println("The weather in " + weatherData.getLocation() + ":");
System.out.println(weatherData.getSkytext());
System.out.println("Temperature: " + weatherData.getTemperature() + "°"
+ weatherData.getDegreeType());
// Access our sub projection
Coordinates coordinates = weatherData.getCoordinates();
System.out.println("The place is located at " + coordinates.getLatitude() + ","
+ coordinates.getLongitude());
}
This works even for creating XML, the XPath expressions can be writable.
SAX parser is working differently with a DOM parser, it neither load any XML document into memory nor create any object representation of the XML document. Instead, the SAX parser use callback function org.xml.sax.helpers.DefaultHandler to informs clients of the XML document structure.
SAX Parser is faster and uses less memory than DOM parser.
See following SAX callback methods :
startDocument() and endDocument() – Method called at the start and end of an XML document.
startElement() and endElement() – Method called at the start and end of a document element.
characters() – Method called with the text contents in between the start and end tags of an XML document element.
XML file
Create a simple XML file.
<?xml version="1.0"?>
<company>
<staff>
<firstname>yong</firstname>
<lastname>mook kim</lastname>
<nickname>mkyong</nickname>
<salary>100000</salary>
</staff>
<staff>
<firstname>low</firstname>
<lastname>yin fong</lastname>
<nickname>fong fong</nickname>
<salary>200000</salary>
</staff>
</company>
XML parser:
Java file Use SAX parser to parse the XML file.
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class ReadXMLFile {
public static void main(String argv[]) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
DefaultHandler handler = new DefaultHandler() {
boolean bfname = false;
boolean blname = false;
boolean bnname = false;
boolean bsalary = false;
public void startElement(String uri, String localName,String qName,
Attributes attributes) throws SAXException {
System.out.println("Start Element :" + qName);
if (qName.equalsIgnoreCase("FIRSTNAME")) {
bfname = true;
}
if (qName.equalsIgnoreCase("LASTNAME")) {
blname = true;
}
if (qName.equalsIgnoreCase("NICKNAME")) {
bnname = true;
}
if (qName.equalsIgnoreCase("SALARY")) {
bsalary = true;
}
}
public void endElement(String uri, String localName,
String qName) throws SAXException {
System.out.println("End Element :" + qName);
}
public void characters(char ch[], int start, int length) throws SAXException {
if (bfname) {
System.out.println("First Name : " + new String(ch, start, length));
bfname = false;
}
if (blname) {
System.out.println("Last Name : " + new String(ch, start, length));
blname = false;
}
if (bnname) {
System.out.println("Nick Name : " + new String(ch, start, length));
bnname = false;
}
if (bsalary) {
System.out.println("Salary : " + new String(ch, start, length));
bsalary = false;
}
}
};
saxParser.parse("c:\\file.xml", handler);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Result
Start Element :company
Start Element :staff
Start Element :firstname
First Name : yong
End Element :firstname
Start Element :lastname
Last Name : mook kim
End Element :lastname
Start Element :nickname
Nick Name : mkyong
End Element :nickname
and so on...
Source(MyKong) - http://www.mkyong.com/java/how-to-read-xml-file-in-java-sax-parser/

How to generate unique element names or auto-increment element names with JAXB?

Is there a way to auto-increment the element names of a Collection when marshalling to an XML using JAXB? I generate an XML using this code:
public void saveTestSettings(final String filename, final DefaultTestSettings t) throws Exception
{
try
{
final Path path = FileSystems.getDefault().getPath((paths.getTestSettingsPath() + filename));
if (Files.notExists(path.getParent()))
throw new Exception(path.getParent().toString() + " does not exist!!");
final OutputStream out = new BufferedOutputStream(Files.newOutputStream(path));
final JAXBContext jaxbContext = JAXBContext.newInstance(DefaultTestSettings.class);
final Marshaller m = jaxbContext.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(t, out);
out.flush();
out.close();
}
catch (JAXBException | IOException e)
{
e.printStackTrace();
throw new Exception(e.getMessage());
}
}
The resulting XML looks like this:
<defaultTestSettings>
<groups>
<instruments>
<name>A200</name>
</instruments>
<name>A0</name>
</groups>
<groups>
<instruments>
<name>A300</name>
</instruments>
<instruments>
<name>A400</name>
</instruments>
<name>A1</name>
</groups>
</defaultTestSettings>
I would like the groups and instruments to be automatically incremented, so that the result looks like this:
<defaultTestSettings>
<groups1>
<instruments1>
<name>A200</name>
</instruments1>
<name>A0</name>
</groups1>
<groups2>
<instruments1>
<name>A300</name>
</instruments1>
<instruments2>
<name>A400</name>
</instruments2>
<name>A1</name>
</groups2>
</defaultTestSettings>
I am not too familiar with using XML schema, would that work?
Below are a couple of way's this use case could be handled:
Option #1 - Using the Standard JAXB APIs
My answer below expands on the excellent answer given by Ilya. I have extended it to use an XmlAdapter to remove the logic from the getElements() method.
Java Model
DefaultSettings
import java.util.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class DefaultSettings {
private List<Groups> groups = new ArrayList<Groups>();
#XmlAnyElement
#XmlJavaTypeAdapter(GroupsAdapter.class)
public List<Groups> getGroups() {
return groups;
}
}
Groups
import java.util.*;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
public class Groups {
private List<Instruments> instruments = new ArrayList<Instruments>();
#XmlAnyElement
#XmlJavaTypeAdapter(InstrumentsAdapter.class)
public List<Instruments> getInstruments() {
return instruments;
}
}
Instruments
public class Instruments {
}
XmlAdapter
GroupsAdapter
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.namespace.QName;
public class GroupsAdapter extends XmlAdapter<JAXBElement<Groups>, Groups> {
private int counter = 1;
private InstrumentsAdapter instrumentsAdapter = new InstrumentsAdapter();
public InstrumentsAdapter getInstrumentsAdapter() {
return instrumentsAdapter;
}
#Override
public Groups unmarshal(JAXBElement<Groups> v) throws Exception {
return v.getValue();
}
#Override
public JAXBElement<Groups> marshal(Groups v) throws Exception {
instrumentsAdapter.resetCounter();
return new JAXBElement<Groups>(new QName("groups" + counter++), Groups.class, v);
}
}
InstrumentsAdapter
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.namespace.QName;
public class InstrumentsAdapter extends XmlAdapter<JAXBElement<Instruments>, Instruments> {
private int counter = 1;
#Override
public Instruments unmarshal(JAXBElement<Instruments> v) throws Exception {
return v.getValue();
}
#Override
public JAXBElement<Instruments> marshal(Instruments v) throws Exception {
return new JAXBElement<Instruments>(new QName("instruments" + counter++), Instruments.class, v);
}
public void resetCounter() {
counter = 1;
}
}
Demo Code
Demo
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(DefaultSettings.class, Groups.class, Instruments.class);
Groups groups1 = new Groups();
groups1.getInstruments().add(new Instruments());
groups1.getInstruments().add(new Instruments());
Groups groups2 = new Groups();
groups2.getInstruments().add(new Instruments());
groups2.getInstruments().add(new Instruments());
DefaultSettings ds = new DefaultSettings();
ds.getGroups().add(groups1);
ds.getGroups().add(groups2);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
GroupsAdapter groupsAdapter = new GroupsAdapter();
marshaller.setAdapter(groupsAdapter);
marshaller.setAdapter(groupsAdapter.getInstrumentsAdapter());
marshaller.marshal(ds, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<defaultSettings>
<groups1>
<instruments1/>
<instruments2/>
</groups1>
<groups2>
<instruments1/>
<instruments2/>
</groups2>
</defaultSettings>
Option #2 - Using EclipseLink JAXB (MOXy)'s #XmlVariableNode Extension
Below is a link to how this use case can be mapped using hte #XmlVariableNode extension that we added in EclipseLink JAXB (MOXy):
http://blog.bdoughan.com/2013/06/mapping-bad-xml-enumerated-collection.html
You can do it with #XmlAnyElement annotation.
Simple example groups tag.
#XmlRootElement(name = "defaultTestSettings")
#XmlSeeAlso(Group.class)
public class DefaultTestSettings
{
private static int counter = 1;
private static final String PROP = "group";
List<Group> groups = new ArrayList<Group>();
#XmlAnyElement
public List<JAXBElement<Group>> getElements()
{
final List<JAXBElement<Group>> retVal = new ArrayList<>();
for (final Group g : groups)
{
retVal.add(new JAXBElement(new QName(PROP + counter++), Group.class, g));
}
return retVal;
}
}

JAXB Marshalling Unmarshalling with CDATA

I am trying to do marshaling with JAXB.
My output is like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<name><![CDATA[<h1>kshitij</h1>]]></name>
<surname><h1>solanki</h1></surname>
<id><h1>1</h1></id>
</root>
...but I need output like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<name><![CDATA[<h1>kshitij</h1>]]></name>
<surname><![CDATA[<h1>solanki</h1>]]></surname>
<id><![CDATA[0]]></id>
</root>
I am using following code to do this.
If I uncomment code I get PropertyBindingException. Without it I can compile but I am not getting the exact required output.
package com.ksh.templates;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import com.sun.xml.bind.marshaller.CharacterEscapeHandler;
public class MainCDATA {
public static void main(String args[])
{
try
{
String name = "<h1>kshitij</h1>";
String surname = "<h1>solanki</h1>";
String id = "<h1>1</h1>";
TestingCDATA cdata = new TestingCDATA();
cdata.setId(id);
cdata.setName(name);
cdata.setSurname(surname);
JAXBContext jaxbContext = JAXBContext.newInstance(TestingCDATA.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(CharacterEscapeHandler.class.getName(), new CharacterEscapeHandler() {
public void escape(char[] ac, int i, int j, boolean flag,
Writer writer) throws IOException {
writer.write( ac, i, j ); }
});
StringWriter stringWriter = new StringWriter();
marshaller.marshal(cdata, stringWriter);
System.out.println(stringWriter.toString());
} catch (Exception e) {
System.out.println(e);
}
}
}
My bean looks like this:
package com.ksh.templates;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.sun.xml.txw2.annotation.XmlCDATA;
#XmlRootElement(name = "root")
#XmlAccessorType(XmlAccessType.FIELD)
public class TestingCDATA {
#XmlElement
#XmlJavaTypeAdapter(value = AdaptorCDATA.class)
private String name;
#XmlElement
#XmlJavaTypeAdapter(value = AdaptorCDATA.class)
private String surname;
#XmlCDATA
public String getName() {
return name;
}
#XmlCDATA
public void setName(String name) {
this.name = name;
}
#XmlCDATA
public String getSurname() {
return surname;
}
#XmlCDATA
public void setSurname(String surname) {
this.surname = surname;
}
}
Adaptor Class
public class AdaptorCDATA extends XmlAdapter<String, String> {
#Override
public String marshal(String arg0) throws Exception {
return "<![CDATA[" + arg0 + "]]>";
}
#Override
public String unmarshal(String arg0) throws Exception {
return arg0;
}
}
You could do the following:
AdapterCDATA
package forum14193944;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class AdapterCDATA extends XmlAdapter<String, String> {
#Override
public String marshal(String arg0) throws Exception {
return "<![CDATA[" + arg0 + "]]>";
}
#Override
public String unmarshal(String arg0) throws Exception {
return arg0;
}
}
Root
The #XmlJavaTypeAdapter annotation is used to specify that the XmlAdapter should be used.
package forum14193944;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlJavaTypeAdapter(AdapterCDATA.class)
private String name;
#XmlJavaTypeAdapter(AdapterCDATA.class)
private String surname;
#XmlJavaTypeAdapter(AdapterCDATA.class)
private String id;
}
Demo
I had to wrap System.out in an OutputStreamWriter to get the desired effect. Also note that setting a CharacterEscapeHandler means that it is responsible for all escape handling for that Marshaller.
package forum14193944;
import java.io.*;
import javax.xml.bind.*;
import com.sun.xml.bind.marshaller.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14193944/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(CharacterEscapeHandler.class.getName(),
new CharacterEscapeHandler() {
#Override
public void escape(char[] ac, int i, int j, boolean flag,
Writer writer) throws IOException {
writer.write(ac, i, j);
}
});
marshaller.marshal(root, new OutputStreamWriter(System.out));
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<name><![CDATA[<h1>kshitij</h1>]]></name>
<surname><![CDATA[<h1>solanki</h1>]]></surname>
<id><![CDATA[0]]></id>
</root>
Please Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
If you use MOXy as your JAXB (JSR-222) provider then you can leverage the #XmlCDATA extension for your use case.
Root
The #XmlCDATA annotation is used to indicate that you want the contents of a field/property wrapped in a CDATA section. The #XmlCDATA annotation can be used in combination with #XmlElement.
package forum14193944;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlCDATA;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlCDATA
private String name;
#XmlCDATA
private String surname;
#XmlCDATA
private String id;
}
jaxb.properties
To use MOXy as your JAXB provider you need to add file named jaxb.properties with the following entry.
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
Below is some demo code to prove that everything works.
package forum14193944;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14193944/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
input.xml/Output
Below is the input to and output from running the demo code.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<name><![CDATA[<h1>kshitij</h1>]]></name>
<surname><![CDATA[<h1>solanki</h1>]]></surname>
<id><![CDATA[0]]></id>
</root>
For More Information
http://blog.bdoughan.com/2010/07/cdata-cdata-run-run-data-run.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
Sorry for digging out this question, and posting a new answer (my rep isn't high enough yet to comment...).
I ran into the same issue, I tried Blaise Doughan's answer, but from my tests, either it doesn't cover all cases, either I'm doing something wrong somewhere.
marshaller.setProperty(CharacterEscapeHandler.class.getName(),
new CharacterEscapeHandler() {
#Override
public void escape(char[] ac, int i, int j, boolean flag,
Writer writer) throws IOException {
writer.write(ac, i, j);
}
});
From my tests, this code removes all escaping, no matter if you are using the #XmlJavaTypeAdapter(AdapterCDATA.class) annotation on your attribute...
To fix that issue, I implemented the following CharacterEscapeHandler :
public class CDataAwareUtfEncodedXmlCharacterEscapeHandler implements CharacterEscapeHandler {
private static final char[] cDataPrefix = "<![CDATA[".toCharArray();
private static final char[] cDataSuffix = "]]>".toCharArray();
public static final CDataAwareUtfEncodedXmlCharacterEscapeHandler instance = new CDataAwareUtfEncodedXmlCharacterEscapeHandler();
private CDataAwareUtfEncodedXmlCharacterEscapeHandler() {
}
#Override
public void escape(char[] ch, int start, int length, boolean isAttVal, Writer out) throws IOException {
boolean isCData = length > cDataPrefix.length + cDataSuffix.length;
if (isCData) {
for (int i = 0, j = start; i < cDataPrefix.length; ++i, ++j) {
if (cDataPrefix[i] != ch[j]) {
isCData = false;
break;
}
}
if (isCData) {
for (int i = cDataSuffix.length - 1, j = start + length - 1; i >= 0; --i, --j) {
if (cDataSuffix[i] != ch[j]) {
isCData = false;
break;
}
}
}
}
if (isCData) {
out.write(ch, start, length);
} else {
MinimumEscapeHandler.theInstance.escape(ch, start, length, isAttVal, out);
}
}
}
If your encoding is not UTF*, you may not want to call MinimumEscapeHandler but rather NioEscapeHandler or even DumbEscapeHandler.
#Test
public void t() throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Root root = new Root();
root.name = "<p>Jorge & Mary</p>";
marshaller.marshal(root, System.out);
}
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public static class Root {
#XmlCDATA
private String name;
}
/* WHAT I SEE IN THE CONSOLE
*
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<name><p>Jorge & Mary</p></name>
</root>
*/
I landed on this page trying to find solution to a similar issue, I found another approach to solve this.
One way to solve this problem is to send XML as SAX2 events to a handler, then write the logic in the handler to add the CDATA tags to the XML. This approach doesn't require any annotation to be added. Useful in scenarios where classes to be marshaled are generated from XSD's.
Suppose you have a String field in a class generated from XSD which is to be marshaled and the String field contains special characters which are to be put inside a CDATA tag.
#XmlRootElement
public class TestingCDATA{
public String xmlContent;
}
We'll start by searching a suitable class whose method can be overridden in our content handler. One such class is XMLWriter found in package com.sun.xml.txw2.output, It's available in jdk 1.7 and 1.8
import com.sun.xml.txw2.output.XMLWriter;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.io.Writer;
import java.util.regex.Pattern;
public class CDATAContentHandler extends XMLWriter {
public CDATAContentHandler(Writer writer, String encoding) throws IOException {
super(writer, encoding);
}
// see http://www.w3.org/TR/xml/#syntax
private static final Pattern XML_CHARS = Pattern.compile("[<>&]");
public void characters(char[] ch, int start, int length) throws SAXException {
boolean useCData = XML_CHARS.matcher(new String(ch, start, length)).find();
if (useCData) {
super.startCDATA();
}
super.characters(ch, start, length);
if (useCData) {
super.endCDATA();
}
}
}
We are overriding the characters method, using regex to check if any special characters are contained. If they are found then we put CDATA tags around them. In this case XMLWriter takes care of adding CDATA tag.
We'll use the following code for marshaling:
public String addCDATAToXML(TestingCDATA request) throws FormatException {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(TestingCDATA.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
StringWriter sw = new StringWriter();
CDATAContentHandler cDataContentHandler = new CDATAContentHandler(sw, "UTF-8");
jaxbMarshaller.marshal(request, cDataContentHandler);
return sw.toString();
} catch (JAXBException | IOException e) {
throw new FormatException("Unable to add CDATA for request", e);
}
}
This would marshal the object and return XML, if we pass a request to be marshaled as mentioned below.
TestingCDATA request=new TestingCDATA();
request.xmlContent="<?xml>";
System.out.println(addCDATAToXML(request)); // Would return the following String
Output-
<?xml version="1.0" encoding="UTF-8"?>
<testingCDATA>
<xmlContent><![CDATA[<?xml>]]></xmlContent>
</testingCDATA>
In addition to #bdoughan answer. Character escape handler with CDATA support
import com.sun.xml.bind.marshaller.CharacterEscapeHandler;
import java.io.IOException;
import java.io.Writer;
/**
* This class is a modern version of JAXB MinimumEscapeHandler with CDATA support
*
* #author me
* #see com.sun.xml.bind.marshaller.MinimumEscapeHandler
*/
public class CdataEscapeHandler implements CharacterEscapeHandler {
private CdataEscapeHandler() {
} // no instanciation please
public static final CharacterEscapeHandler theInstance = new CdataEscapeHandler();
#Override
public void escape(char[] ch, int start, int length, boolean isAttVal, Writer out) throws IOException {
// avoid calling the Writerwrite method too much by assuming
// that the escaping occurs rarely.
// profiling revealed that this is faster than the naive code.
int limit = start + length;
for (int i = start; i < limit; i++) {
if (!isAttVal
&& i <= limit - 12
&& ch[i] == '<'
&& ch[i + 1] == '!'
&& ch[i + 2] == '['
&& ch[i + 3] == 'C'
&& ch[i + 4] == 'D'
&& ch[i + 5] == 'A'
&& ch[i + 6] == 'T'
&& ch[i + 7] == 'A'
&& ch[i + 8] == '[') {
int cdataEnd = i + 8;
for (int k = i + 9; k < limit - 2; k++) {
if (ch[k] == ']'
&& ch[k + 1] == ']'
&& ch[k + 2] == '>') {
cdataEnd = k + 2;
break;
}
}
out.write(ch, start, cdataEnd + 1);
if (cdataEnd == limit - 1) return;
start = i = cdataEnd + 1;
}
char c = ch[i];
if (c == '&' || c == '<' || c == '>' || c == '\r' || (c == '\"' && isAttVal)) {
if (i != start)
out.write(ch, start, i - start);
start = i + 1;
switch (ch[i]) {
case '&':
out.write("&");
break;
case '<':
out.write("<");
break;
case '>':
out.write(">");
break;
case '\"':
out.write(""");
break;
}
}
}
if (start != limit)
out.write(ch, start, limit - start);
}
}
com.sun.internal dont works with play2,but this works
private static String marshal(YOurCLass xml){
try{
StringWriter stringWritter = new StringWriter();
Marshaller marshaller = JAXBContext.newInstance(YourCLass.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "ISO-8859-1");
marshaller.marshal(xml, stringWritter);
return stringWritter.toString().replaceAll("<", "<").replaceAll(">", ">");
}
catch(JAXBException e){
throw new RuntimeException(e);
}
}

JAXB unmarshalling multiple XML elements into single class

I have the following XML structure, which is modelling a single concept across multiple XML elements. This format is not in my control.
<Output>
<Wrapper>
<Channel>
<id>1</id>
<type>x</type>
</Channel>
<Channel>
<id>2</id>
<type>y</type>
</Channel>
<ChannelName>
<id>1</id>
<name>Channel name</name>
</ChannelName>
<ChannelName>
<id>2</id>
<name>Another channel name</name>
</ChannelName>
</Wrapper>
</Output>
I want to model this in a database that I do have control over and can have a more simple Channel table with id, type and name fields. Therefore I would like to unmarshal into a single List<Channel> on the Wrapper class.
Can this be done with #Xml... annotations automatically? I am currently using JAXB to unmarshal into separate #XmlElement(name="Channel") and #XmlElement(name="ChannelName") class lists and then post-processing the transient ChannelName/name on the Channel but I am thinking there must be an easier automated way to map these elements. Or is it a job for XSLT?
It might help to know that the XML is coming in as an HTTP file POST file and I'm using Spring 3, Java and Hibernate. I'm hoping something in EclipseLink JAXB (MOXy) might help :)
#XmlElementWrapper will do the job:
#XmlElementWrapper(name="Wrapper")
#XmlElement(name="Channel")
private List<Channel> channels;
For more advanced cases you can use the #XmlPath extension in EclipseLink JAXB (MOXy):
http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html
Here is what I have so far. I'm still trying to eliminate the need for the helper objects. This example requires EclipseLink JAXB (MOXy).
Model Objects
Your model objects are:
package example;
import java.util.ArrayList;
import java.util.List;
public class Wrapper {
private List<Channel> channels = new ArrayList<Channel>();
public List<Channel> getChannels() {
return channels;
}
public void setChannels(List<Channel> channels) {
this.channels = channels;
}
}
and:
package example;
import javax.xml.bind.annotation.XmlID;
public class Channel {
private String id;
private String type;
private String name;
#XmlID
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Helper Objects
My current solution involves some helper objects:
package example.adapted;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import example.Channel;
import example.Wrapper;
#XmlRootElement(name="Output")
#XmlType(propOrder={"channels", "channelNames"})
public class AdaptedWrapper {
private Wrapper wrapper = new Wrapper();
private List<ChannelName> channelNames;
#XmlTransient
public Wrapper getWrapper() {
for(ChannelName channelName : channelNames) {
channelName.getChannel().setName(channelName.getName());
}
return wrapper;
}
#XmlElementWrapper(name="Wrapper")
#XmlElement(name="Channel")
public List<Channel> getChannels() {
return wrapper.getChannels();
}
public void setChannels(List<Channel> channels) {
wrapper.setChannels(channels);
}
#XmlElementWrapper(name="Wrapper")
#XmlElement(name="ChannelName")
public List<ChannelName> getChannelNames() {
return channelNames;
}
public void setChannelNames(List<ChannelName> channelNames) {
this.channelNames = channelNames;
}
}
and:
package example.adapted;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlIDREF;
import example.Channel;
public class ChannelName {
private String name;
private Channel channel;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlIDREF
#XmlElement(name="id")
public Channel getChannel() {
return channel;
}
public void setChannel(Channel channel) {
this.channel = channel;
}
}
Demo Code
package example;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import example.adapted.AdaptedWrapper;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(AdaptedWrapper.class);
File xml = new File("input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
AdaptedWrapper adaptedWrapper = (AdaptedWrapper) unmarshaller.unmarshal(xml);
Wrapper wrapper = adaptedWrapper.getWrapper();
for(Channel channel : wrapper.getChannels()) {
System.out.println(channel.getName());
}
}
}
You can save your coding time by automating this process in JAXB:
Create a XML schema for your XML using the link below as save it as output.xsd file:
http://www.xmlforasp.net/CodeBank/System_Xml_Schema/BuildSchema/BuildXMLSchema.aspx
Run the batch script file (name it as output.bat) below from the project root folder (.) using JDK as only JDK has xjc.exe tool (fill in the necessary details):
"C:\Program Files\Java\jdk1.6.0_24\bin\xjc.exe" -p %1 %2 -d %3
where...
syntax: output.bat %1 %2 %3
%1 = target package name
%2 = full file path name of the generated XML schema .xsd
%3 = root source folder to store generated JAXB java files
Example:
let say project folder is organized as follows:
.
\_src
Run following at command prompt from (.):
output.bat com.project.xml .\output.xsd .\src
It will create a few files:
.
\_src
\_com
\_project
\_xml
|_ObjectFactory.java
|_Output.java
Then, you can create a few useful methods below to manipulate Output objects:
private JAXBContext jaxbContext = null;
private Unmarshaller unmarshaller = null;
private Marshaller marshaller = null;
public OutputManager(String packageName) {
try {
jaxbContext = JAXBContext.newInstance(packageName);
unmarshaller = jaxbContext.createUnmarshaller();
marshaller = jaxbContext.createMarshaller();
} catch (JAXBException e) {
}
}
public Output loadXML(InputStream istrm) {
Output load = null;
try {
Object o = unmarshaller.unmarshal(istrm);
if (o != null) {
load = (Output) o;
}
} catch (JAXBException e) {
JOptionPane.showMessageDialog(null, e.getLocalizedMessage(), e.getClass().getSimpleName(), JOptionPane.ERROR_MESSAGE);
}
return load;
}
public void saveXML(Object o, java.io.File file) {
Output save = null;
try {
save = (Output) o;
if (save != null) {
marshaller.marshal(save, file);
}
} catch (JAXBException e) {
JOptionPane.showMessageDialog(null, e.getLocalizedMessage(), e.getClass().getSimpleName(), JOptionPane.ERROR_MESSAGE);
}
}
public void saveXML(Object o, FileOutputStream ostrm) {
Output save = null;
try {
save = (Output) o;
if (save != null) {
marshaller.marshal(save, ostrm);
}
} catch (JAXBException e) {
JOptionPane.showMessageDialog(null, e.getLocalizedMessage(), e.getClass().getSimpleName(), JOptionPane.ERROR_MESSAGE);
}
}

Categories