How to resolve unmarshalling exception? - jaxb

I am trying to learn JAXB. I created sample as below, but during unmarshalling i am getting exception. My files are below. Can you help me to resolve?
AddRequest.java:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "AddRequest", namespace = "http://www.example.org/AddRequest", propOrder = {
"first",
"sec",
"any"
})
public class AddRequest {
#XmlElement(name = "First")
protected int first;
#XmlElement(name = "Sec")
protected int sec;
#XmlAnyElement(lax = true)
protected List<Object> any;
}
ObjectFactory.java
#XmlRegistry
public class ObjectFactory {
private final static QName _AddRequest_QNAME = new QName("http://www.example.org/AddRequest", "AddRequest");
public ObjectFactory() {
}
public AddRequest createAddRequest() {
return new AddRequest();
}
#XmlElementDecl(namespace = "http://www.example.org/AddRequest", name = "AddRequest")
public JAXBElement<AddRequest> createAddRequest(AddRequest value) {
return new JAXBElement<AddRequest>(_AddRequest_QNAME, AddRequest.class, null, value);
}
}
package-info.java
#javax.xml.bind.annotation.XmlSchema(namespace = "http://www.example.org/AddRequest", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package org.example.addrequest;
Main.java
try {
File file = new File("C:\\Users\\nbkyooh\\IBM\\rationalsdp\\workspace\\Sample\\resource\\AddRequest.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(org.example.addrequest.AddRequest.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
jaxbUnmarshaller.unmarshal(file);
} catch (JAXBException e) {
e.printStackTrace();
}
AddRequest.xml
<?xml version="1.0" encoding="UTF-8"?>
<tns:AddRequest xmlns:tns="http://www.example.org/AddRequest" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/AddRequest AddRequest.xsd ">
<tns:First>0</tns:First>
<tns:Sec>0</tns:Sec>
</tns:AddRequest>
I am getting exception as below, what i have done wrong. I used all the generated files.
javax.xml.bind.UnmarshalException: Unexpected element "{http://www.example.org/AddRequest}AddRequest". Expected elements are "".
at com.ibm.xml.xlxp2.jaxb.msg.JAXBMessageProvider.throwUnmarshalExceptionWrapper(JAXBMessageProvider.java:93)
at com.ibm.xml.xlxp2.jaxb.unmarshal.impl.DeserializationContext.handleSkippedRootElementEvent(DeserializationContext.java:318)
at com.ibm.xml.xlxp2.jaxb.unmarshal.impl.JAXBDocumentScanner.produceRootElementEvent(JAXBDocumentScanner.java:189)

try this:
try {
FileInputStream inputStream = new FileInputStream(new File("your file"));
AddRequest req = JAXB.unmarshal(inputStream, AddRequest.class);
} catch (FileNotFoundException e) {
e.printStackTrace();
}

In your use case you have define the root element information leveraging the #XmlElementDecl annotation on a ObjectFactory class annotated with #XmlRegistry (see: http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html). Since the class annotated with #XmlRegistry can be called anything and JAXB doesn't do package scanning you need to include your ObjectFactory as one of the classes passed in to bootstrap the JAXBContext.
JAXBContext.newInstance(AddRequest.class, ObjectFactory.class);
Since the ObjectFactory class references AddRequest you could simplify this down to:
JAXBContext.newInstance(ObjectFactory.class);

Related

JaxB representation for Object to support different types

Following java property can be of either String/Integer type. I tried adding annotation as below, but it would only take the last type(string in this case), even when an integer is passed. How can we allow multiple types for this property?
#XmlElements({
#XmlElement(type = Integer.class), #XmlElement(type = String.class)
})
private Object val1;
The problem is that there is no name to differentiate between the different type of classes when unmarshaling. I don't know what exactly are your requirements but this is how you could make it work:
#XmlRootElement(name = "root")
#XmlAccessorType(XmlAccessType.FIELD)
public class SomeRoot {
#XmlElements({
#XmlElement(name="string", type = String .class),
#XmlElement(name="int", type = Integer.class)
})
private Object val;
public SomeRoot(Object val) {
this.val = val;
}
public SomeRoot() { }
}
When you have a String value provided it would be marshaled into something like that:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<string>test</string>
</root>
And when given an Integer it would marshal into something like that:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<int>1</int>
</root>
Both of these would be unmarshaled to their proper type because of the difference in the element name.
Also if you would have a list of Objects you could marshal/unmarshal something like this with no problem:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<int>1</int>
<string>test1</string>
<string>test2</string>
<string>test3</string>
<int>3</int>
</root>
Update:
If you want both types under the <val> element, you could use a custom XmlAdapter to check on the type. This could look like this:
public class MyXmlAdapter extends XmlAdapter<Object, Object> {
private DocumentBuilder documentBuilder;
public MyXmlAdapter() {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
documentBuilder = dbf.newDocumentBuilder();
} catch(Exception e) {
// TODO - Handle Exception
}
}
#Override
public Object unmarshal(Object v) throws Exception {
Element element = (Element) v;
String elementValue = element.getTextContent();
if(StringUtils.isNumeric(elementValue)) {
return Integer.parseInt(elementValue);
} else {
return new String(elementValue);
}
}
#Override
public Object marshal(Object v) throws Exception {
return v;
}
}
And your root class would be something like this:
#XmlRootElement(name = "root")
#XmlAccessorType(XmlAccessType.FIELD)
public class SomeRoot {
#XmlJavaTypeAdapter(MyXmlAdapter.class)
private Object val;
public SomeRoot(Object val) {
this.val = val;
}
public SomeRoot() { }
public Object getVal() {
return val;
}
}
Trying this out in a simple main, resolves the provided xml to the right type for both String and Integer on val:
public static void main(String[] args) {
try {
File file = new File("file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(SomeRoot.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
jaxbUnmarshaller.setAdapter(new MyXmlAdapter());
SomeRoot pojo = (SomeRoot) jaxbUnmarshaller.unmarshal(file);
System.out.println(pojo.getVal().getClass());
} catch (JAXBException e) {
e.printStackTrace();
}
}

Is there a way for JAXB to give a warning about this... overridden properties in subclasses?

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."

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

Cannot unmarshall using #XmlAttribute

I can marshall my sitemap with JAXB, though I cannot unmarshall it! Any help would be much appreciated.
I get the following exception:
javax.xml.bind.UnmarshalException: unexpected element
(uri:"http://www.sitemaps.org/schemas/sitemap/0.9", local:"urlset").
Expected elements are <{}urls>,<{}urlset>
Sitemap urlset:
#XmlRootElement(name = "urlset")
public class XMLURLSet
{
List<XMLURL> urls;
final String xmlns = "http://www.sitemaps.org/schemas/sitemap/0.9";
#XmlAttribute(name = "xmlns")
public String getXmlns() {
return xmlns;
}
public void setXmlns(String xmlns) {
// nop
}
#XmlElement(name = "url")
public List<XMLURL> getUrls(){
return urls;
}
public void setUrls(List<XMLURL> urls) {
this.urls = urls;
}
Sitemap urls:
#XmlRootElement(name = "urls")
public class XMLURL {
String loc;
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
}
Output XML (it's correct):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://www.example.com</loc>
</url>
</urlset>
JUnit marshalling code that works
JAXBContext context = JAXBContext.newInstance(XMLURLSet.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
StringWriter sw = new StringWriter();
marshaller.marshal(urlSet, sw);
String xml = sw.toString();
Unmarshalling that DOESNT WORK (cont' from above):
JAXBContext ctx = JAXBContext.newInstance(XMLURLSet.class);
Unmarshaller umsler = ctx.createUnmarshaller();
XMLURLSet xmlUrlSet = (XMLURLSet) umsler.unmarshal(new StreamSource(new StringReader(xml)));
The last line generates the exception:
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.sitemaps.org/schemas/sitemap/0.9", local:"urlset"). Expected elements are <{}urls>,<{}urlset>
The problem is with the way you are trying to namespace qualify your document. You will need to add a class called package-info in the same package as your domain classes with the following entry:
package-info
#XmlSchema(
namespace = "http://www.sitemaps.org/schemas/sitemap/0.9",
elementFormDefault = XmlNsForm.QUALIFIED)
package your.package.name;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

Resources