Here is my conversion code. This is taking long time when we are dealing with large data... Calling the method almost a million times... We could clearly see that it is holding threads for a while.
Please suggest me some ways to improve the performance!
public class GenericObjectXMLConverter<T> {
private T t = null;
private static JAXBContext jaxbContext =null;
public GenericObjectXMLConverter() {
}
public GenericObjectXMLConverter(T obj){
t = obj;
}
protected final Logger log = Logger.getLogger(getClass());
/**
* Converts the java Object and into a xml string message type.
* #param object the object to convert
* #return String the converted xml message as string
*/
public String objectToXMLMessage(T object) {
StringWriter stringWriter = new StringWriter();
//JAXBContext jaxbContext=null;
try {
jaxbContext = JAXBContext.newInstance(object.getClass());
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.marshal(object, stringWriter);
} catch (JAXBException e) {
log.warn("JAXBException occured while converting the java object into xml string :"+e.getMessage());
}
/*if(log.isDebugEnabled())
log.debug("xml string after conversion:"+stringWriter.toString());*/
return stringWriter.toString();
}
/**
* Converts a xml string message into a Java Object
* #param string the string message to convert
* #return Object the result as Java Object. If the message parameter is null then
* this method will simply return null.
*/
#SuppressWarnings("unchecked")
public T xmlToObject(String message) {
if(message.equals("") || message.equals(" ") || message.length()==0){
return null;
}else{
T object=null;
try {
jaxbContext = JAXBContext.newInstance(t.getClass());
StringReader reader = new StringReader(message);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
object = (T)jaxbUnmarshaller.unmarshal(reader);
} catch (JAXBException e) {
log.warn("JAXBException occured while converting the xml string into a Java Object :"+e.getMessage());
}
/* if(log.isDebugEnabled()){
log.debug("Java object after conversion:"+object.toString());
}*/
return object;
}
}
}
Performance and JAXB Runtime Classes
You should avoid creating the same JAXBContext over and over. JAXBContext is thread safe and should be reused to improve performance.
Marshaller/Unmarshaller are not thread safe, but are quick to create. It's not as big a deal to reuse them.
You should create single JAXBContext object per your bean class.
Here is my version to maintain singleton object of JAXBContext per bean class.
public class MyJAXBUtil {
public static final Map<String, JAXBContext> JAXB_MAP = new HashMap<>();
public static JAXBContext getJAXBContext(Object object) {
if(JAXB_MAP.get(object.getClass().getCanonicalName()) != null) {
return JAXB_MAP.get(object.getClass().getCanonicalName());
}else {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(object.getClass());
JAXB_MAP.put(object.getClass().getCanonicalName(), jaxbContext);
return jaxbContext;
} catch (JAXBException e) {
e.printStackTrace();
return null;
}
}
}
}
You can call getJAXBContext method when you need JAXBContext of your bean class and create Marshaller/Unmarshaller locally.
Related
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>
I am developing a jax-rs web service which will consume XML. Because of a business requirement I need both the XML and the unmarshalled java object. So instead of adding the java type as the method parameter, I am using String as the type and injecting the XML stream into the string.
private static JAXBContext context;
public StudentFacade() throws JAXBException {
if(context == null) {
context = JAXBContext.newInstance(Student.class);
}
}
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public Response sendSms(final String xml) throws JAXBException {
XMLInputFactory xif = XMLInputFactory.newFactory();
XMLStreamReader xsr = xif.createXMLStreamReader(new ByteArrayInputStream(xml.getBytes()));
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.unmarshal(xsr);
....
}
My question, should I create a new XMLInputFactory and Unmarshaller always?
Is the below code valid if I move XMLInputFactory and Unmarshaller to constructor and initialize them only once like the JAXBContext
private static Unmarshaller unmarshaller;
private static XMLInputFactory xif;
public StudentFacade() throws JAXBException {
if(unmarshaller == null) {
JAXBContext context = JAXBContext.newInstance(Student.class);
unmarshaller = context.createUnmarshaller();
xif = XMLInputFactory.newFactory();
}
}
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public Response sendSms(final String xml) throws JAXBException {
XMLStreamReader xsr = xif.createXMLStreamReader(new ByteArrayInputStream(xml.getBytes()));
unmarshaller.unmarshal(xsr);
....
}
I ran into an interesting JAXB issue that had me puzzled for a while, since no errors were produced, just wasn't seeing the output I expected.
We have several classes that all extends a Base class.
At one point I introduced an object property into the base class, that also happened to be a property defined in just ONE of the subclasses. This caused any setter of this property from ANY subclasses (outside the one with that property overridden) to have this property completely ignored in the XML output.
For example:
#XmlRootElement(name = "animal")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({Dog.class, Fish.class})
public class Animal {
protected String movement;
....
}
#XmlRootElement(name = "dog")
#XmlSeeAlso({Animal.class})
#XmlAccessorType(XmlAccessType.FIELD)
public class Dog extends Animal {
private String breed;
protected String movement;
....
}
#XmlRootElement(name = "fish")
#XmlSeeAlso({Animal.class})
#XmlAccessorType(XmlAccessType.FIELD)
public class Fish extends Animal {
private String scaleType;
....
}
public static void main(String[] args) {
Dog dog = new Dog();
dog.setBreed("lab");
dog.setMovement("walks");
String xml = AnimalJaxb.toXml(dog);
System.out.println("dog = "+xml);
Fish fish = new Fish();
fish.setMovement("swims"); //WILL NOT SHOW UP IN XML!
fish.setScaleType("normal");
String xml = AnimalJaxb.toXml(fish);
System.out.println("fish = "+xml);
//to and from XML looks like...
public static <T> T fromXml(String xml, Class<T> clazz) throws Exception {
ByteArrayInputStream input = null;
Unmarshaller u = null;
try {
input = new ByteArrayInputStream(xml.getBytes());
JAXBContext jc = JAXBContext.newInstance(clazz);
u = jc.createUnmarshaller();
} catch (Exception e) {
throw e;
} finally {
input.close();
}
return (T) u.unmarshal(input);
}
public static String toXml(Object obj) throws Exception {
StringWriter sw = new StringWriter();
try {
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(obj, sw);
return sw.toString();
} catch (Exception e) {
throw e;
} finally {
sw.close();
}
}
When the above runs. The "movement" property on Fish will NOT be in the XML. To fix this, I need to remove the overridden movement property in the Dog class.
What's frustrating is JAXB doesn't throw any errors or complain. The only way it will complain is if I make the base class (Animal) abstract and mark it #XmlTransient, afterwhich JAXB will complain about having "two properties with the same name."
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.
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);
}