How to persist UserData of Element in JAXB Unmarshal? - jaxb

I unmarshal the Document to object as below.
Before that, when parsing XML, use setUserData to store location information for each element.
class MyJaxbAdapter extends XmlAdapter<Object, SubObject> {}
#Override
public UnattendComponent unmarshal(Object v) throws Exception {
Node node = (Node) v; // ElementNSImpl; It's probably a newly created object. Because It is different from the document object given by ownerDocument as SAXSource.
node.getUserData(...) // return NULL
}
}
Document document = ...;
unmarshaller.setAdapter(new MyJaxbAdapter());
MyXMLObject object = unmarshaller.unmarshal(new DOMSource(document), MyXMLObject.class).getValue();
But I can't get UserData inside XmlAdapter's unmarshal method. Is there any way to persist UserData?
Locator information is stored in the properties of Element as shown below.
#Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
Element el = document.createElementNS(usedNamespaceUri, qName);
// ...
el.setUserData(
ElementUserData.class.getName(),
ElementUserData.builder()
.lineNumber(locator.getLineNumber())
.columnNumber(locator.getColumnNumber())
.build(),
null);
}
I need the Locator information (UserData) stored by the above code in the unmarshal of the XmlAdapter.
However, there is no userdata in the node passed as an argument to unmarshal .
Sample Code:
https://github.com/joseph-jclab/jaxb-question-01

Not entirely sure if this is something you are looking for but providing it as a reference so you might get some idea to proceed further:
Sample XML:
<root>
<name>Batman</name>
<year>2008</year>
</root>
Root.class:
#XmlRootElement(name = "root")
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
private String name;
private String year;
#XmlJavaTypeAdapter(CustomAdapter.class)
private String after;
private void afterUnmarshal(Unmarshaller m, Object parent) {
after = name;
}
}
CustomAdapter.class:
public class CustomAdapter extends XmlAdapter<String, String> {
#Override
public String unmarshal(String v) throws Exception {
System.out.println("Within Unmarshal : " + v);
return null;
}
#Override
public String marshal(String v) throws Exception {
System.out.println("Within Marshal : " + v);
return null;
}
}
SampleMain.class:
public class SampleMain {
public static void main(String[] args) throws XMLStreamException, JAXBException {
final InputStream inputStream = Unmarshalling.class.getClassLoader().getResourceAsStream("sample.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Root.class).createUnmarshaller();
unmarshaller.setAdapter(new CustomAdapter());
final Root root = unmarshaller.unmarshal(xmlStreamReader, Root.class).getValue();
System.out.println(root.toString());
Marshaller marshaller = JAXBContext.newInstance(Root.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(root, System.out);
}
}
Output:
Root(name=Batman, year=2008, after=Batman)
Within Marshal : Batman
<root>
<name>Batman</name>
<year>2008</year>
</root>

Related

#XmlPath(".") conflicts with #XmlAdapter

having this Jaxb Xml definition, i try to remove the Map Elements Wrapper by adding #XmlPath(".") but it cause exception during the unmarchaling
#XmlRootElement
public abstract class ViewElement{
#XmlJavaTypeAdapter(value=EventAdapter.class)
public Map<Event, String> getEvents() {
}
private transient Class entityType;
public Class getEntityType() {
return entityType;
}
}
And the EventAdapter is
public class EventAdapter extends XmlAdapter<EventAdapter.AdaptedMap, Map<Event, String>> {
public static class AdaptedMap {
#XmlVariableNode("key")
List<AdaptedEntry> entries = new ArrayList<AdaptedEntry>();
}
public static class AdaptedEntry {
#XmlTransient
public String key;
#XmlValue
public String value;
}
.....
}
my output was
<element>
<events>
<onCellEdit>do some thing<onCellEdit>
</events>
<entityType>com.agitech.erp.model.erp.ErpFolder</entityType>
<element>
I try to remove the <events> tag by adding #XmlPath(".")
#XmlPath(".")
#XmlJavaTypeAdapter(value=EventAdapter.class)
public Map<Event, String> getEvents() {
}
The output is good
<element>
<onCellEdit>do some thing<onCellEdit>
<entityType>com.agitech.erp.model.erp.ErpFolder</entityType>
<element>
but the unmarchaling faileds
Caused by: Exception [EclipseLink-3002] (Eclipse Persistence Services - 2.6.0.v20140809-296a69f): org.eclipse.persistence.exceptions.ConversionException
Exception Description: The object [], of class [class java.lang.String], from mapping [org.eclipse.persistence.oxm.mappings.XMLDirectMapping[entityType-->view.entityType/text()]] with descriptor [XMLDescriptor(com.agitech.erp.view.BeanView --> [DatabaseTable(view), DatabaseTable(viewFrame), DatabaseTable(viewElement)])], could not be converted to [class java.lang.Class].
Internal Exception: java.lang.ClassNotFoundException:
at org.eclipse.persistence.exceptions.ConversionException.couldNotBeConvertedToClass(ConversionException.java:95)
at org.eclipse.persistence.internal.helper.ConversionManager.convertObjectToClass(ConversionManager.java:446)
Debuging Jaxb bring me to the line
org.eclipse.persistence.internal.oxm.XMLDirectMappingNodeValue
public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord) {
...
line 205 unmarshalRecord.setAttributeValue(convertedValue, xmlDirectMapping);
}
During the unmarchaling of entityType value, the UnmarshalRecordImpl.currentObj contains the EventAdapter instead of the parent element
I modify org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl
public XPathNode getNonAttributeXPathNode(String namespaceURI, String localName, String qName, Attributes attributes) {
....
if(null == resultNode && null == nonPredicateNode) {
// ANY MAPPING
resultNode = xPathNode.getAnyNode();
// by default it return the EventAdapter, changing it to NULL fix my problem
}
....
}
Not a safe solution
I have been able to reproduce the issue that you are seeing, but haven't yet worked out the cause. You can use the following bug to track the progress on this issue:
http://bugs.eclipse.org/457169
After trying a lot of things, I was able to find a workaround for this issue. I thought of posting here the same so it can be helpful to someone else in the future. The lead has confirmed the issue around 5 years ago but seems like they have not fixed it and I was facing a similar issue.
Basically, we can use the beforeMarshal and afterUnmarshal methods to change the values in the fields.
You need to create a field List<Object> with #XmlAnyElement(lax=true) along with Map<String,Object>.
Remove the #XmlPath(".") and the XMLAdapter class.
Mark the field Map<String, Object> with #XmlTransient.
Now within the beforeMarshal and afterMarshal fields, you can exchange the data. During the unmarshal in beforeunmarshal, all the unknown field values will be present within the List<Object> loop over it and add it to the Map<String, Object>.
Similarly during the marshaling, you can move the values Map<String, Object> to List<Object> by creating the DOM elements.
Marshaling all values are added to root as DOM Elements are present and during Unmarshaling known values are read first and then-unknown values are stored within List<Object> due to #XmlAnyElement.
I have created an example using the Customer class, you can modify it accordingly for your need.
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#XmlRootElement(name = "Customer")
#XmlType(name = "Customer", propOrder = {"name", "age", "otherElements"})
#XmlAccessorType(XmlAccessType.FIELD)
#Getter
#Setter
#AllArgsConstructor
#ToString
#NoArgsConstructor
public class Customer {
#XmlTransient
private String isA;
private String name;
private String age;
#XmlAnyElement(lax = true)
#JsonIgnore
private List<Object> otherElements = new ArrayList<>();
#JsonIgnore
#XmlTransient
private Map<String, Object> userExtensions = new HashMap<>();
#JsonAnyGetter
#JsonSerialize(using = CustomExtensionsSerializer.class)
public Map<String, Object> getUserExtensions() {
return userExtensions;
}
#JsonAnySetter
public void setUserExtensions(String key, Object value) {
userExtensions.put(key, value);
}
private void beforeMarshal(Marshaller m) throws ParserConfigurationException {
System.out.println("Before Marshalling User Extension: " + userExtensions);
ExtensionsModifier extensionsModifier = new ExtensionsModifier();
otherElements = extensionsModifier.Marshalling(userExtensions);
System.out.println("Before Marshalling Final Other Elements " + otherElements);
userExtensions = new HashMap<>();
}
private void afterUnmarshal(Unmarshaller m, Object parent) throws ParserConfigurationException {
System.out.println("After Unmarshalling : " + otherElements);
ExtensionsModifier extensionsModifier = new ExtensionsModifier();
userExtensions = extensionsModifier.Unmarshalling(otherElements);
otherElements = new ArrayList();
}
}
You can refer the creation of DOM ELEMENTS here:https://stackoverflow.com/a/24239105/7584240
You can refer my complete answer here: https://stackoverflow.com/a/67923216/7584240

Creating null object for input StringReader using JAXBContext

I am trying to create bean from string but unable to create as it is returning null.Here is my code
public ModelAndView checkPhotoQualityRequest(
#RequestBody String photoDataXml, HttpServletRequest request) {
PhotoQuality photoQuality = null;
try {
JAXBContext jaxbContext = JAXBContext
.newInstance(PhotoQuality.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(photoDataXml);
photoQuality = (PhotoQuality) unmarshaller.unmarshal(reader);
PhotoQuality.java
package in.gov.uid.opencvaccess.bean;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlType(name = "PhotoQuality")
#XmlRootElement(name = "PhotoQuality")
public class PhotoQuality {
private String photoid;
private byte[] photo;
private boolean quality;
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getPhotoid() {
return photoid;
}
public void setPhotoid(String photoid) {
this.photoid = photoid;
}
public byte[] getPhoto() {
return photo;
}
public void setPhoto(byte[] photo) {
this.photo = photo;
}
public boolean isQuality() {
return quality;
}
public void setQuality(boolean quality) {
this.quality = quality;
}
}
Please help me to sort out this issue.When I debug and check bean its showing all null values but photoDataXml showing complete xml.
I have found the reason.When I tried using RESTClient its giving null object. But as soon as I written client code and passed StringWriter value created from PhotoQuality Object then it runs properly.

javassist not injecting annotation at existing field

I'm trying to inject JAXB annotation at runtime using Javassist. I have written following code:
public class AssistAnnotationInjector {
public static void addAnnotationRunTime(String className, String fieldName) throws NotFoundException, CannotCompileException, IOException, ClassNotFoundException{
CtClass ctClass = ClassPool.getDefault().get(className);
ClassFile ccFile = ctClass.getClassFile();
ConstPool constPool = ccFile.getConstPool();
AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation annot = new Annotation("javax.xml.bind.annotation.XmlTransient",constPool);
attr.addAnnotation(annot);
CtField field = ctClass.getDeclaredField(fieldName);
field.getFieldInfo().addAttribute(attr);
System.out.println(field.getAnnotation(XmlTransient.class));
ccFile.setVersionToJava5();
ctClass.writeFile();
}
public static void main (String args[]) throws CannotCompileException, NotFoundException, IOException, SecurityException, NoSuchMethodException, ClassNotFoundException, JAXBException, NoSuchFieldException{
Person<Student> p = new Person<Student>();
p.setName("XYZ");
Student s = new Student();
s.setName("ABC");
s.setId("239423");
p.setPayload(s);
addAnnotationRunTime("RuntimeAnnotation.Person", "name");
Field f = p.getClass().getDeclaredField("name");
System.out.println(f.getAnnotation(XmlTransient.class));
JAXBContext context = JAXBContext.newInstance(p.getClass());
Marshaller mr = context.createMarshaller();
mr.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
mr.marshal(p, System.out);
}
}
And Person.java class is:
#XmlRootElement(name="Person")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({Student.class})
public class Person <T>{
private T payload;
private String name;
public void setPayload(T payload){
this.payload = payload;
}
public T getPayload(){
return payload;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
In AssistAnnotationInjector.java, I am trying to add XmlTransient annotation to 'name' field. But the name field is still coming in marshalling output. Why is it so?
PS: marshal output is :
#javax.xml.bind.annotation.XmlTransient
null
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Person>
<payload xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="student">
<name>ABC</name>
<id>239423</id>
</payload>
**<name>XYZ</name>**
</Person>
name tag was not expected to present in output..
You basicaly have 2 options:
do the modification before you load the class. You can not use reflection in the normal way! One can try to use org.reflections with Maven plugin to pre-fetch classes. See here for more info.
use custom classloader to load the modified class. See here for more info.
After adding the attribute to the field you need to call ctClass.toClass() method,which freezes the class. After this you can check for the annotation.

xs:int unmarshalled to null for decimal values

I've got a problem related to the unmarshalling process in a JAX-WS based WebService. In WSDL file there is an element defined as
<element name="quantity" nillable="true" type="int" />
In the related JAVA class it is defined as:
#XmlElement(name = "Quantity", required = true, type = Integer.class, nillable = true)
protected Integer quantity;
When an XML value for this element is the representation of a decimal number (3.4), the element is unmarshalled as a null Integer. No SOAPFault is generated and it's impossible to distinguish decimal values from null values inside the WebService.
Could it be a defect in JAXB implementation or I'm doing something wrong?
Could it be a defect in JAXB implementation or I'm doing something
wrong?
This is not a defect in the JAXB (JSR-222) implementation. It is a result of how the JAX-WS is configured to use JAXB. I will demonstrate below with an example.
Root
Below is a domain object with a field that matches the one from your question. I have remove the type=Integer.class from the #XmlElement annotation since it is redundant.
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElement(name = "Quantity", required = true, nillable = true)
protected Integer quantity;
}
Demo
JAXB offers the ability to set a ValidationEventHandler on the Unmarshaller to give you some control over how unmarshal errors are handled.
import java.io.StringReader;
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();
unmarshaller.setEventHandler(new ValidationEventHandler() {
#Override
public boolean handleEvent(ValidationEvent event) {
System.out.println(event.getMessage());
return true;
}
});
StringReader xml = new StringReader("<root><Quantity>3.4</Quantity></root>");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
In the expert group we decided that invalid element data is common and that JAXB should not fail out every time this is encountered, but you can see that a ValidationEvent is raised.
Not a number: 3.4
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<Quantity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</root>
Update Demo
If we update the ValidationEventHandler to indicate that we do not wish to continue the unmarshal when a ValidationEvent is raised we can make the following change.
#Override
public boolean handleEvent(ValidationEvent event) {
System.out.println(event.getMessage());
return false;
}
Updated Output
And now the following output occurs.
Not a number: 3.4
Exception in thread "main" javax.xml.bind.UnmarshalException: Not a number: 3.4
- with linked exception:
[java.lang.NumberFormatException: Not a number: 3.4]
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:647)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleError(UnmarshallingContext.java:676)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleError(UnmarshallingContext.java:672)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.handleParseConversionException(Loader.java:256)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.LeafPropertyLoader.text(LeafPropertyLoader.java:54)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.text(UnmarshallingContext.java:499)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.processText(SAXConnector.java:166)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.endElement(SAXConnector.java:139)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:606)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1742)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2900)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:607)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:116)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:489)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:835)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:123)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1210)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:568)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:203)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:175)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:157)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:214)
at forum14741140.Demo.main(Demo.java:22)
Caused by: java.lang.NumberFormatException: Not a number: 3.4
at com.sun.xml.internal.bind.DatatypeConverterImpl._parseInt(DatatypeConverterImpl.java:101)
at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$17.parse(RuntimeBuiltinLeafInfoImpl.java:713)
at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$17.parse(RuntimeBuiltinLeafInfoImpl.java:711)
at com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor$CompositeTransducedAccessorImpl.parse(TransducedAccessor.java:232)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.LeafPropertyLoader.text(LeafPropertyLoader.java:50)
... 19 more
I answered this question in : https://stackoverflow.com/a/30617814/3632201
I have been struggling with this issue during the last week and finally i have managed a working solution. The trick is that JAXB looks for the methods beforeUnmarshal and afterUnmarshal in the object annotated with #XmlRootElement.
..
#XmlRootElement(name="MSEPObtenerPolizaFechaDTO")
#XmlAccessorType(XmlAccessType.FIELD)
public class MSEPObtenerPolizaFechaDTO implements Serializable {
..
public void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) throws JAXBException, IOException, SAXException {
unmarshaller.setSchema(Utils.getSchemaFromContext(this.getClass()));
unmarshaller.setEventHandler(new CustomEventHandler());
}
public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) throws JAXBException {
unmarshaller.setSchema(null);
unmarshaller.setEventHandler(null);
}
Using this ValidationEventHandler:
public class CustomEventHandler implements ValidationEventHandler{
#Override
public boolean handleEvent(ValidationEvent event) {
if (event.getSeverity() == event.ERROR ||
event.getSeverity() == event.FATAL_ERROR)
{
ValidationEventLocator locator = event.getLocator();
throw new RuntimeException(event.getMessage(), event.getLinkedException());
}
return true;
}
}
}
And this is the metodh getSchemaFromContext created in your Utility class:
#SuppressWarnings("unchecked")
public static Schema getSchemaFromContext(Class clazz) throws JAXBException, IOException, SAXException{
JAXBContext jc = JAXBContext.newInstance(clazz);
final List<ByteArrayOutputStream> outs = new ArrayList<ByteArrayOutputStream>();
jc.generateSchema(new SchemaOutputResolver(){
#Override
public Result createOutput(String namespaceUri,
String suggestedFileName) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
outs.add(out);
StreamResult streamResult = new StreamResult(out);
streamResult.setSystemId("");
return streamResult;
}
});
StreamSource[] sources = new StreamSource[outs.size()];
for (int i = 0; i < outs.size(); i++) {
ByteArrayOutputStream out = outs.get(i);
sources[i] = new StreamSource(new ByteArrayInputStream(out.toByteArray()), "");
}
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
return sf.newSchema(sources);
}

Can JAXB/MOXy serialize enums in the same way as regular classes (non-enums)?

Imagine I have enum defined like this:
public enum ArchiveStatus implements Serializable {
CANDIDATE (0, "CANDIDATE", "Candidate for archival"),
IN_LIBRARY (1, "IN-LIBRARY", ".."),
FROM_LIBRARY (2, "FROM-LIBRARY", "..");
private int id;
private String shortName;
private String longName;
public ArchiveStatus( int id, String shortName, String longName ) {
..
}
public int getId() { .. }
public String getShortName() { .. }
public String getLongName() { .. }
}
By default MOXy is going to serialize it to JSON like this:
{
..
"archiveStatus": "CANDIDATE",
..
}
Is there a way to configure MOXy (in the mapping file) to serialize the enum like a regular class:
{
..
"archiveStatus": { "id" : 0, "shortName": "CANDIDATE", "longName": "Candidate for archival" },
..
}
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
ArchiveStatusAdapter
You can solve this use case by leveraging an XmlAdapter. XmlAdapter is a JAXB mechanism that allows you to marshal one type of object as another.
package forum10144489;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class ArchiveStatusAdapter extends XmlAdapter<ArchiveStatusAdapter.AdaptedArchiveStatus, ArchiveStatus> {
public static class AdaptedArchiveStatus {
public int id;
public String shortName;
public String longName;
}
#Override
public ArchiveStatus unmarshal(AdaptedArchiveStatus adaptedArchiveStatus) throws Exception {
if(null == adaptedArchiveStatus) {
return null;
}
return ArchiveStatus.valueOf(adaptedArchiveStatus.shortName);
}
#Override
public AdaptedArchiveStatus marshal(ArchiveStatus archiveStatus) throws Exception {
if(null == archiveStatus) {
return null;
}
AdaptedArchiveStatus adaptedArchiveStatus = new AdaptedArchiveStatus();
adaptedArchiveStatus.id = archiveStatus.getId();
adaptedArchiveStatus.longName = archiveStatus.getLongName();
adaptedArchiveStatus.shortName = archiveStatus.getShortName();
return adaptedArchiveStatus;
}
}
Root
The XmlAdapter can be specified at the field, property, type, or package level using the #XmlJavaTypeAdapter annotation.
package forum10144489;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
public class Root {
private ArchiveStatus archiveStatus;
#XmlJavaTypeAdapter(ArchiveStatusAdapter.class)
public ArchiveStatus getArchiveStatus() {
return archiveStatus;
}
public void setArchiveStatus(ArchiveStatus archiveStatus) {
this.archiveStatus = archiveStatus;
}
}
jaxb.properties
To specify MOXy as your JAXB provider you need to add a file called jaxb.properties in the same package as your domain classes with the following entry.
javax.xml.bind.context.factory = org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
package forum10144489;
import java.io.StringReader;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(2);
properties.put("eclipselink.media-type", "application/json");
properties.put("eclipselink.json.include-root", false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {Root.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StringReader jsonStringReader = new StringReader("{\"archiveStatus\" : {\"id\" : 0, \"shortName\" : \"CANDIDATE\", \"longName\" : \"Candidate for archival\"}}");
StreamSource jsonSource = new StreamSource(jsonStringReader);
Root root = unmarshaller.unmarshal(jsonSource, Root.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
Below is the output from running the demo code:
{
"archiveStatus" : {
"id" : 0,
"shortName" : "CANDIDATE",
"longName" : "Candidate for archival"
}
}
For More Information
http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
http://blog.bdoughan.com/search/label/XmlAdapter
http://blog.bdoughan.com/search/label/jaxb.properties

Resources