I use REST and i was wondering if i can tell jaxb to insert a string field "as-it-is" into the outgoing xml.
Certainly i count unpack it before returning, but i would like to save this step.
#XmlRootElement(name="unnestedResponse")
public class Response{
#Insert annotation here ;-)
private String alreadyXml;
private int otherDate; ...
}
Is there a possability to tell JAXB to just use the String as it is without escapting? I want that the client does not have to parse my response and then parse this field.
greetings,
m
You can use the #XmlAnyElement and specify a DomHandler to keep a portion of the XML document as a String.
Customer
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Customer {
private String bio;
#XmlAnyElement(BioHandler.class)
public String getBio() {
return bio;
}
public void setBio(String bio) {
this.bio = bio;
}
}
BioHandler
import java.io.*;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.annotation.DomHandler;
import javax.xml.transform.Source;
import javax.xml.transform.stream.*;
public class BioHandler implements DomHandler<String, StreamResult> {
private static final String BIO_START_TAG = "<bio>";
private static final String BIO_END_TAG = "</bio>";
private StringWriter xmlWriter = new StringWriter();
public StreamResult createUnmarshaller(ValidationEventHandler errorHandler) {
return new StreamResult(xmlWriter);
}
public String getElement(StreamResult rt) {
String xml = rt.getWriter().toString();
int beginIndex = xml.indexOf(BIO_START_TAG) + BIO_START_TAG.length();
int endIndex = xml.indexOf(BIO_END_TAG);
return xml.substring(beginIndex, endIndex);
}
public Source marshal(String n, ValidationEventHandler errorHandler) {
try {
String xml = BIO_START_TAG + n.trim() + BIO_END_TAG;
StringReader xmlReader = new StringReader(xml);
return new StreamSource(xmlReader);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
For More Information
http://blog.bdoughan.com/2011/04/xmlanyelement-and-non-dom-properties.html
Following bdoughan's answer did not work for me as I encountered errors during marshalling when the text contained the '& character (e.g. in URLs or when using HTML entities such as e.g. " ").
I was able to resolve this by changing the custom DomHandler's marshal method to
public Source marshal(String et, ValidationEventHandler veh) {
Node node = new SimpleTextNode(et);
return new DOMSource(node);
}
where SimpleTextNode implements the Node interface as follows:
class SimpleTextNode implements Node {
String nodeValue = "";
#Override
public SimpleTextNode(String nodeValue) {
this.nodeValue = nodeValue;
}
#Override
public short getNodeType() {
return TEXT_NODE;
}
// the remaining methods of the Node interface are not needed during marshalling
// you can just use the code template of your IDE...
...
}
PS: I would have loved to leave this as a comment to bdoughan's answer, but unfortunately I have way too little reputation :-(
Related
I am using SaxParser to read the large complex XML file. I do not wish to create the model class as I do not know the exact data which will be coming in the XML so I am trying to find if there is a generic way of reading the XML data using some sort of Context.
I have used a similar approach for JSON using the Jackson, which worked very well for me. Since I am new to Sax Parser, I cannot completely understand how to achieve the same. for complex inner values, I am unable to establish a parent-child relationship and I am unable to build relationships between tags and attributes.
Following is the code I have so far:
ContextNode my generic class to store all XML information using the parent-child relationships.
#Getter
#Setter
#ToString
#NoArgsConstructor
public class ContextNode {
protected String name;
protected String value;
protected ArrayList<ContextNode> children = new ArrayList<>();
protected ContextNode parent;
//Constructor 1: To store the simple field information.
public ContextNode(final String name, final String value) {
this.name = name;
this.value = value;
}
//Constructor 2: To store the complex field which has inner elements.
public ContextNode(final ContextNode parent, final String name, final String value) {
this(name, value);
this.parent = parent;
}
Following is my method to parse XML using SAX within EventReader.class
public class EventReader{
//Method to read XML events and create pre-hash string from it.
public static void xmlParser(final InputStream xmlStream) {
final SAXParserFactory factory = SAXParserFactory.newInstance();
try {
final SAXParser saxParser = factory.newSAXParser();
final SaxHandler handler = new SaxHandler();
saxParser.parse(xmlStream, handler);
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
}
}
Following is my SaxHandler:
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import java.util.HashMap;
public class SaxHandler extends DefaultHandler {
private final List<String> XML_IGNORE_FIELDS = Arrays.asList("person:personDocument","DocumentBody","DocumentList");
private final List<String> EVENT_TYPES = Arrays.asList("person");
private Map<String, String> XML_NAMESPACES = null;
private ContextNode contextNode = null;
private StringBuilder currentValue = new StringBuilder();
#Override
public void startDocument() {
ConstantEventInfo.XML_NAMESPACES = new HashMap<>();
}
#Override
public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) {
//For every new element in XML reset the StringBuilder.
currentValue.setLength(0);
if (qName.equalsIgnoreCase("person:personDocument")) {
// Add the attributes and name-spaces to Map
for (int att = 0; att < attributes.getLength(); att++) {
if (attributes.getQName(att).contains(":")) {
//Find all Namespaces within the XML Header information and save it to the Map for future use.
XML_NAMESPACES.put(attributes.getQName(att).substring(attributes.getQName(att).indexOf(":") + 1), attributes.getValue(att));
} else {
//Find all other attributes within XML and store this information within Map.
XML_NAMESPACES.put(attributes.getQName(att), attributes.getValue(att));
}
}
} else if (EVENT_TYPES.contains(qName)) {
contextNode = new ContextNode("type", qName);
}
}
#Override
public void characters(char ch[], int start, int length) {
currentValue.append(ch, start, length);
}
#Override
public void endElement(final String uri, final String localName, final String qName) {
if (!XML_IGNORE_FIELDS.contains(qName)) {
if (!EVENT_TYPES.contains(qName)) {
System.out.println("QName : " + qName + " Value : " + currentValue);
contextNode.children.add(new ContextNode(qName, currentValue.toString()));
}
}
}
#Override
public void endDocument() {
System.out.println(contextNode.getChildren().toString());
System.out.println("End of Document");
}
}
Following is my TestCase which will call the method xmlParser
#Test
public void xmlReader() throws Exception {
final InputStream xmlStream = getClass().getResourceAsStream("/xmlFileContents.xml");
EventReader.xmlParser(xmlStream);
}
Following is the XML I need to read using a generic approach:
<?xml version="1.0" ?>
<person:personDocument xmlns:person="https://example.com" schemaVersion="1.2" creationDate="2020-03-03T13:07:51.709Z">
<DocumentBody>
<DocumentList>
<Person>
<bithTime>2020-03-04T11:00:30.000+01:00</bithTime>
<name>Batman</name>
<Place>London</Place>
<hobbies>
<hobby>painting</hobby>
<hobby>football</hobby>
</hobbies>
<jogging distance="10.3">daily</jogging>
<purpose2>
<id>1</id>
<purpose>Dont know</purpose>
</purpose2>
</Person>
</DocumentList>
</DocumentBody>
</person:personDocument>
Providing the answer as it can be helpful to someone in the future:
First we need to create a class ContextNode which can hold the information:
#Getter
#Setter
public class ContextNode {
protected String name;
protected String value;
protected ArrayList<ContextNode> attributes = new ArrayList<>();
protected ArrayList<ContextNode> children = new ArrayList<>();
protected ContextNode parent;
protected Map<String, String> namespaces;
public ContextNode(final ContextNode parent, final String name, final String value) {
this.parent = parent;
this.name = name;
this.value = value;
this.namespaces = parent.namespaces;
}
public ContextNode(final Map<String, String> namespaces) {
this.namespaces = namespaces;
}
public ContextNode(final Map<String, String> namespaces) {
this.namespaces = namespaces;
}
}
Then we can read the XML and store the information in the context node:
import lombok.Getter;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import java.security.NoSuchAlgorithmException;
import java.util.*;
public class SaxHandler extends DefaultHandler {
//Variables needed to store the required information during the parsing of the XML document.
private final Deque<String> path = new ArrayDeque<>();
private final StringBuilder currentValue = new StringBuilder();
private ContextNode currentNode = null;
private ContextNode rootNode = null;
private Map<String, String> currentAttributes;
private final HashMap<String, String> contextHeader = new HashMap<>();
#Override
public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) {
//Put every XML tag within the stack at the beginning of the XML tag.
path.push(qName);
//Reset attributes for every element
currentAttributes = new HashMap<>();
//Get the path from Deque as / separated values.
final String p = path();
//If the XML tag contains the Namespaces or attributes then add to respective Namespaces Map or Attributes Map.
if (attributes.getLength() > 0) {
//Loop over every attribute and add them to respective Map.
for (int att = 0; att < attributes.getLength(); att++) {
//If the attributes contain the : then consider them as namespaces.
if (attributes.getQName(att).contains(":") && attributes.getQName(att).startsWith("xmlns:")) {
contextHeader.put(attributes.getQName(att).substring(attributes.getQName(att).indexOf(":") + 1), attributes.getValue(att));
} else {
currentAttributes.put(attributes.getQName(att), attributes.getValue(att).trim());
}
}
}
if (rootNode == null) {
rootNode = new ContextNode(contextHeader);
currentNode = rootNode;
rootNode.children.add(new ContextNode(rootNode, "type", qName));
} else if (currentNode != null) {
ContextNode n = new ContextNode(currentNode, qName, (String) null);
currentNode.children.add(n);
currentNode = n;
}
}
#Override
public void characters(char[] ch, int start, int length) {
currentValue.append(ch, start, length);
}
#Override
public void endElement(final String uri, final String localName, final String qName) {
try {
System.out.println("completed reading");
System.out.println(rootNode);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
rootNode = null;
//At the end of the XML element tag reset the value for next element.
currentValue.setLength(0);
//After completing the particular element reading, remove that element from the stack.
path.pop();
}
private String path() {
return String.join("/", this.path);
}
}
You may need to make some additional changes based on your particular requirement. This is just a sample that gives some idea.
So Im pretty new to Java coming from C#! They are pretty similar programming languages, so im getting a hang of it quickly, but there is a problem I have been battling with for a while, that I hope you can help me solve!
So Im using my SAX parser to parse the XML document, and it works fine, but Im having problems parsing the whole xml document, and don't know how to parse the attribute value in the top element.
My xml document is as follows:
This is the code snippet where I believe the problem lies! This code works for parsing all of tecaj elements and their attributes/content values, but not "datum" attribute:
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
{
this.elementStack.push(qName);
CurrencyModel model = new CurrencyModel();
if("tecajnica".equals(qName))
{
if(attributes != null)
{
model.setDatum(attributes.getValue(0));
}
}
else if("tecaj".equals(qName))
{
if(attributes != null)
{
model.setOznaka(attributes.getValue(0));
model.setSifra(attributes.getValue(1));
}
}
this.objectStack.push(model);
}
So I have a model class that looks like this:
public class CurrencyModel
{
public String getDatum() {
return datum;
}
public void setDatum(String datum) {
this.datum = datum;
}
public String getOznaka() {
return oznaka;
}
public void setOznaka(String oznaka) {
this.oznaka = oznaka;
}
public String getSifra() {
return sifra;
}
public void setSifra(String sifra) {
this.sifra = sifra;
}
public double getValue() {
return value;
}
public void setValue(double value) {
this.value = value;
}
String datum;
String oznaka;
String sifra;
double value;
#Override
public String toString() {
return "CurrencyModel{" +
"datum=" + datum +
", oznaka='" + oznaka + '\'' +
", sifra='" + sifra + '\'' +
", value=" + value +
'}';
}
}
So each object of type CurrencyModel has its date property that is supposed to get the value of the attribute from its respected "tecajnica" element. It works for all of the other properties but "Datum". At first I was parsing it as Date type, but as that didn't work I tried parsing it as a String. Now it works without any errors, but always sets the object "Datum" property to null...
Output looks as follows:
Any help and suggestions will be much appreciated!!! Thank you in advance!
You can use JAXB parser instead of SAX, It converts each element tag into a Java objects and easily configurable too. But we need to create classes for each element tag in the XML file as mentioned in this article https://www.javatpoint.com/jaxb-tutorial
As per your data your root class will be like below:
package com.a.b.c;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="DtecBs")
public class DtecBs {
private String datum;
private List<tecjnica> tecjnicaList;
#XmlAttribute
public String getDatum() {
return datum;
}
public void setDatum(String datum) {
this.datum = datum;
}
#XmlElement(name="tecjnica")
public List<tecjnica> getTecjnicaList() {
return tecjnicaList;
}
public void setTecjnicaList(List<tecjnica> tecjnicaList) {
this.tecjnicaList = tecjnicaList;
}
}
Following method helps to convert XML into JavaObject:
Required parameters are:
InputStream (Inputstream of the XML file)
Class (Class name of the root element in the XML com.a.b.c.DtecBs)
DtecBs dtecbsObj = (DtecBs)convertXmltoJavaObject(is,className);
public Object convertXmltoJavaObject(InputStream is, Class className) throws JAXBException, ParserConfigurationException, SAXException {
//Disable XXE
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
//Do unmarshall operation
Source xmlSource = new SAXSource(spf.newSAXParser().getXMLReader(),new InputSource(is));
JAXBContext jaxbContext = JAXBContext.newInstance(className);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
JAXBIntrospector ji = jaxbContext.createJAXBIntrospector();
return ji.getValue(jaxbUnmarshaller.unmarshal(xmlSource));
}
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");
}
}
I am using Jackson for JSON serialization of a list of objects.
Here is what I get:
{"ArrayList":[{"id":1,"name":"test name"}]}
But I want this :
{"rootname":[{"id":1,"name":"test name"}]} // ie showing the string I want as the root name.
Below is my approach to this:
Interface:
public interface MyInterface {
public long getId();
public String getName();
}
Implementation class:
#JsonRootName(value = "rootname")
public class MyImpl implements MyInterface {
private final long id;
private String name;
public MyImpl(final long id,final name) {
this.id = id;
this.name = name;
}
// getters
}
JSon serialization:
public class MySerializer {
public static String serializeList(final List<MyInterface> lists) {
//check for null value.Throw Exception
final ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
return mapper.writeValueAsString(lists);
}
}
Test:
final List<MyInterface> list = new ArrayList<MyImpl>();
MyImpl item = new MyImpl(1L,"test name");
list.add(item);
final String json = MySerializer.serializeList(list);
System.out.println(json);
Here is what I get:
{"ArrayList":[{"id":1,"name":"test name"}]}
But I want this :
{"rootname":[{"id":1,"name":"test name"}]} // ie showing the string I want as the root name.
I have tried all suggested solutions I could find but failed to achieve my goal. I have looked at:
Jackson : custom collection serialization to JSON
How do I rename the root key of a JSON with Java Jackson?
Jackson : custom collection serialization to JSON
Or am I missing something? I am using jackson 1.9.12 for this. Any help in this regard is welcome.
Well, by default Jackson uses one of two annotations when trying to determine the root name to be displayed for wrapped values - #XmlRootElement or #JsonRootName. It expects this annotation to be on the type being serialized, else it will use the simple name of the type as the root name.
In your case, you are serializing a list, which is why the root name is 'ArrayList' (simple name of the type being serialized). Each element in the list may be of a type annotated with #JsonRootName, but the list itself is not.
When the root value you are trying to wrap is a collection then you need some way of defining the wrap name:
Holder/Wrapper Class
You can create a wrapper class to hold the list, with an annotation to define the desired property name (you only need to use this method when you do not have direct control of the ObjectMapper/JSON transformation process):
class MyInterfaceList {
#JsonProperty("rootname")
private List<MyInterface> list;
public List<MyInterface> getList() {
return list;
}
public void setList(List<MyInterface> list) {
this.list = list;
}
}
final List<MyInterface> lists = new ArrayList<MyInterface>(4);
lists.add(new MyImpl(1L, "test name"));
MyInterfaceList listHolder = new MyInterfaceList();
listHolder.setList(lists);
final String json = mapper.writeValueAsString(listHolder);
Object Writer
This is the preferable option. Use a configured ObjectWriter instance to generate the JSON. In particular, we are interested in the withRootName method:
final List<MyInterface> lists = new ArrayList<MyInterface>(4);
lists.add(new MyImpl(1L, "test name"));
final ObjectWriter writer = mapper.writer().withRootName("rootName");
final String json = writer.writeValueAsString(lists);
I know, I am late , but I have better approach which don't require Holder/Wrapper Class. It picks root key from annotation.
package com.test;
import com.fasterxml.jackson.annotation.JsonRootName;
#JsonRootName("Products")
public class ProductDTO {
private String name;
private String description;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
Here is test class:-
package com.test;
import java.io.IOException;
import java.util.ArrayList;
import org.junit.Test;
import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class ProductDTOTestCase {
#Test
public void testPersistAndFindById() throws JsonGenerationException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
ProductDTO productDTO = new ProductDTO();
productDTO.setDescription("Product 4 - Test");
ArrayList<ProductDTO> arrayList = new ArrayList<ProductDTO>();
arrayList.add(productDTO);
String rootName = ProductDTO.class.getAnnotation(JsonRootName.class).value();
System.out.println(mapper.writer().withRootName(rootName).writeValueAsString(arrayList));
}
}
It will give following output
{"Products":[{"name":null,"description":"Product 4 - Test"}]}
#JsonTypeName("usuarios")
#JsonTypeInfo(include= JsonTypeInfo.As.WRAPPER_OBJECT,use= JsonTypeInfo.Id.NAME)
public class UsuarioDT extends ArrayList<Usuario> {
#JsonProperty("rowsAffected")
private Integer afectados;
public Integer getAfectados() {
return afectados;
}
public void setAfectados(Integer afectados) {
this.afectados = afectados;
}
}
You need to use this annotation at the top of the class
#JsonTypeName("rootname")
SimpleXML can serialize a Java Enum fine but when it comes to de-serialization, it returns null instead of creating Enum from the generated XML. Is it something I am doing wrong of Enum serialization is not supported at all?
Serialization returns this:
<TestStatus>
<status>Functional</status>
</TestStatus>
Test Enum:
#Root
public enum TestStatus {
AVAILABLE("Functional"),
NOT_AVAILABLE("Dysfunctional");
#Element
private String status;
private Status(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
}
How do you serialize your enum?
if you use it like this, it should work without problems but will return some different XML:
Example:
#Root
public class Example
{
#Element
private TestStatus status = TestStatus.AVAILABLE;
// ...
}
Test:
final File f = new File("test.xml");
Serializer ser = new Persister();
ser.write(new Example(), f);
Example m = ser.read(Example.class, f);
XML:
<example>
<status>AVAILABLE</status>
</example>
You can rename the xml-tags with annotationarguments, but the value wont be changeable.
Another (possible) solution is using a custom converter:
Annotations of the enum:
#Root()
#Convert(TestStatusConverter.class)
public enum TestStatus
{
// ...
}
Converter (Example)
public class TestStatusConverter implements Converter<TestStatus>
{
#Override
public TestStatus read(InputNode node) throws Exception
{
final String value = node.getNext("status").getValue();
// Decide what enum it is by its value
for( TestStatus ts : TestStatus.values() )
{
if( ts.getStatus().equalsIgnoreCase(value) )
return ts;
}
throw new IllegalArgumentException("No enum available for " + value);
}
#Override
public void write(OutputNode node, TestStatus value) throws Exception
{
// You can customize your xml here (example structure like your xml)
OutputNode child = node.getChild("status");
child.setValue(value.getStatus());
}
}
Test (enum):
final File f = new File("test.xml");
// Note the new Strategy
Serializer ser = new Persister(new AnnotationStrategy());
ser.write(TestStatus.AVAILABLE, f);
TestStatus ts = ser.read(TestStatus.class, f);
System.out.println(ts);
Test (class with enum):
As above but with AnnotationStrategy
You don't need to add annotations to enums, they serialize automatically.