Im struggling with the jaxb2marshaller configuration to work with classes generated from wsdl file.
My jaxb2marshaller
public Jaxb2Marshaller marshaller() {
final Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPaths("org.otheruri", "org.tempuri");
return marshaller;
}
I prepare a structure of classes and pass them thru marshaller.marshal to get the xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:ValidateResponse xmlns="http://otheruri.org" xmlns:ns2="http://tempuri.org/">
<ns2:ValidateResult>
<ArrayOfPerson>
<Person><NawNumber>personNAW</NawNumber></Person>
</ArrayOfPerson>
</ns2:ValidateResult>
</ns2:ValidateResponse>
But when I take this xml and run marshaller.unmarshall the ValidateResult.arrayOfPerson is null. There is no stacktrace or anything.
My generated classes look like this
package org.tempuri
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"validateResult"
})
#XmlRootElement(name = "ValidateResponse")
public class ValidateResponse {
#XmlElementRef(name = "ValidateResult", namespace = "http://tempuri.org/", type = JAXBElement.class, required = false)
protected JAXBElement<org.otheruri.ValidateResponse> validateResult;
public JAXBElement<org.otheruri.ValidateResponse> getValidateResult() {
return validateResult;
}
public void setValidateResult(JAXBElement<org.otheruri.ValidateResponse> value) {
this.validateResult = value;
}
}
and
package org.otheruri
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "ValidateResponse", propOrder = {
"persons",
"userLoggedIn"
})
public class ValidateResponse {
#XmlElementRef(name = "Persons", namespace = "http://otheruri.org", type = JAXBElement.class, required = false)
protected JAXBElement<ArrayOfPerson> persons;
#XmlElementRef(name = "UserLoggedIn", namespace = "http://otheruri.org", type = JAXBElement.class, required = false)
protected JAXBElement<ValidateCredentialsResponse> userLoggedIn;
public JAXBElement<ArrayOfPerson> getPersons() {
return persons;
}
public void setPersons(JAXBElement<ArrayOfPerson> value) {
this.persons = value;
}
public JAXBElement<ValidateCredentialsResponse> getUserLoggedIn() {
return userLoggedIn;
}
public void setUserLoggedIn(JAXBElement<ValidateCredentialsResponse> value) {
this.userLoggedIn = value;
}
}
EDIT
So after time and lots of debbuging I know what is wrong. For unknown reason the created xml has ArrayOfPerson node (like class name) instead of Persons (that is specified in #XmlElementRef). Still I dont know why this is happening
So in the end it appeared that my generated object factory had 2 methods that returned same JaxbObject and accepted ArrayOfPerson object and very similar name, but had different name specified in the #XmlElementDecl, and of course I used the wrong one to generate xml, so I could not consume it later. :/
Related
I am having the typical 'unexpected element (uri:"", local:"stickynote")' JAXB error, although it only shows when I set the event handler on the unmarshaller object. When the event handler is not set code runs fine, but the specific data needed is not unmarshalled.
I have worked on the problem for a couple of days, including many Internet searches, and am not hitting on the right solution. I feel like I am missing something simple, but so far it is eluding me.
Notes:
I am using Java JDK 8 with Apache NetBeans 15.
The original code was generated with the xjc command from schemas we receive from multiple vendors. I cannot modify the schemas and have to figure out how to use them "as is".
I wrote the code below to duplicate the issue and simplify the posted code. Import statements have been removed for brevity. If they are needed please let me know.
Since inheritance is involved in one of the classes, I added the appropriate #XmlSeeAlso annotation. That did not help.
This is my first real foray into JAXB. It seems to be perfect for this project. I just have to figure out how to get it to work.
First, example XML:
<?xml version="1.0" encoding="UTF-8"?>
<documents>
<document>
<notes>
<stickynote id="123">test note</stickynote>
</notes>
</document>
</documents>
Code to create the JAXB context and create the Unmarshaller object:
JAXBContext context = JAXBContext.newInstance( Documents.class );
Unmarshaller u = context.createUnmarshaller();
u.setEventHandler(new DefaultValidationEventHandler());
JAXBElement< Documents > root =
u.unmarshal(
new StreamSource( new File( FILENAME )),
Documents.class );
The corresponding classes to handle each element:
Documents class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "document" })
#XmlRootElement(name = "documents", namespace = "http://www.example.org/documents")
public class Documents {
protected Document document;
public Document getDocument() {
return document;
}
public void setDocument(Document document) {
this.document = document;
}
}
Document class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "notes" })
#XmlRootElement(name = "document", namespace = "http://www.example.org/documents")
public class Document {
protected NotesType notes;
public NotesType getNotes() {
return notes;
}
public void setNotes(NotesType notes) {
this.notes = notes;
}
}
NotesType class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "NotesType", propOrder = { "notes" })
public class NotesType {
protected List<NoteType> notes;
public List<NoteType> getNotes() {
if ( isNull( notes )) {
notes = new ArrayList<>();
}
return this.notes;
}
}
NoteType class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "NoteType", propOrder = { "note" })
#XmlSeeAlso({ StickyNote.class })
public class NoteType {
#XmlAttribute(name = "id", required = true)
protected String id;
protected String note;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
And finally, the StickyNote class, which extends the NoteType class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "subType" })
public class StickyNote extends NoteType {
protected String subType = "sticky";
public String getSubType() {
return subType;
}
public void setSubType(String subType) {
this.subType = subType;
}
}
The exception is:
DefaultValidationEventHandler: [ERROR]: unexpected element (uri:"", local:"stickynote"). Expected elements are <{}notes>
Location: line 5 of file:/C:/Test/documents.xml
javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"stickynote"). Expected elements are <{}notes>
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:726)
*and so on*
Thanks in advance for any and all suggestions and help!
john
My issue turned out to be how I was using xjc. I was building each schema we received individually, not realizing there was some overlap in the schemas due to the vendors using other vendors schemas. This caused issues when one vendor's schema was built early in the process and then another vendor's schema was built after it that used the earlier vendor's schema.
Building all the schemas as a group, instead of individually, allowed xjc to "see" everything it needed to see and the corresponding code worked as expected.
I have the following JAXB entity:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = EntityConstants.PARTNER)
public class FilePartner
{
#XmlAttribute(name = EntityConstants.IDENTIFIER, required = true)
private String identifier;
#XmlElement(name = EntityConstants.NAME)
private String name;
#XmlElement(name = EntityConstants.ROOT_PATH)
private String rootPath;
...
}
which serialized into a similar structure:
<file-partner identifier="foo">
<name>bar</name>
<root-path>path123</root-path>
...
</file-partner>
I also have an entity which represents a list of partners:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = EntityConstants.PARTNERS)
public class FilePartnerList
{
#XmlElement(name = EntityConstants.PARTNER)
private List<FilePartner> partners = new ArrayList<FilePartner>();
public List<FilePartner> getPartners()
{
return partners;
}
public void addPartners(List<FilePartner> partners)
{
this.partners.addAll(partners);
}
}
which serializes into:
<partners>
<file-partner identifier="foo">
...
</file-partner>
<file-partner identifier="foo2">
...
</file-partner>
...
</partners>
I am looking for a way to force the jaxb unmarshaller to deserialize XMLs in the form of
<file-partner identifier="foo">
<name>bar</name>
<root-path>path123</root-path>
...
</file-partner>
into FilePartnerList instances with list size of 1, i.e:
JAXBContext context = JAXBContext.newInstance(FilePartner.class, FilePartnerList.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
InputStream inputStream = getResourceAsStream(filePartnerAsXml);
FilePartnerList partnerList = (FilePartnerList) unmarshaller.unmarshal(inputStream); // This SHOULD be unmarshalled to FilePartnerList instead of FilePartner
assertTrue(partnerList.getPartners().getSize().equals(1));
How do I achieve that?
private List<FilePartner> partners = new ArrayList<FilePartner>(**1**);
That way you'll get a fixed size input array....
But I'm sure that you will want to get the XSD of that model with the 'maxocurrs=1' so you will end modifying the XSD manually.
Anyway: why don't you, if the list size must be fixed to '1', simply set it as a simple node with a single child? Something like this (untested):
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = EntityConstants.PARTNERS)
public class FilePartnerList
{
#XmlElement(name = EntityConstants.PARTNER)
private FilePartner partners;
public FilePartner getFilePartner()
{
return partner;
}
public void setPartner(FilePartner partner)
{
this.partner = partner;
}
}
This way you will have one and only one partner per parnets-list.
The XML that fullfits the XSD of your service a text and in that text is indistinguible a list with max size 1 and a node.
I'm getting null value when unmarshelling the xml file to java class. But the xml file as values corresponding to the its attributes. Is there any mistake in my pojo class or unmarshelling?
Please help
This is my pojo
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "")
#XmlRootElement(name = "CardUpdateResponse",namespace="http://www.samople.com/Prepaid")
public class FVCardUpdateResponse {
#XmlElement(name = "AccountNumber")
private String AccountNumber;
#XmlElement(name = "ResCode")
private String ResCode;
#XmlElement(name = "ResErrorCode")
private String ResErrorCode;
#XmlElement(name = "ResErrorMsg")
private String ResErrorMsg;
//Setters and Getters
}
This is my xml file
<?xml version="1.0" encoding="UTF-8"?>
<CardUpdateResponse xmlns="http://www.samople.com/Prepaid">
<CARDUPDATE_RET>
<ResErrorMsg>ID Issue Date must be equal or less than present date</ResErrorMsg>
<ResErrorCode>ErrIsud01</ResErrorCode>
<ResCode>0</ResCode>
<ACCOUNTNUMBER>2000000003918246</ACCOUNTNUMBER>
</CARDUPDATE_RET>
</CardUpdateResponse>
this is code for unmarshelling
public class XmlUnmarshelling {
public void unmarshell()
{
try
{
System.out.println("xml unmarshelling class");
File file = new File("D:/var/lib/tomcat7/webapps/tmpFiles/1.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(FVCardUpdateResponse.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
FVCardUpdateResponse CARDUPDATE_ret = (FVCardUpdateResponse) jaxbUnmarshaller.unmarshal(file);
System.out.println("xml unmarshelled = "+CARDUPDATE_ret.getResErrorMsg());//Getting null value as response.
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Your POJO doesn't have the same structure as your XML. CardUpdateResponse doesn't directly contain the properties in your POJO, it contains CARDUPDATE_RET element which contains the properties.
You could modify your POJO like this to match the XML:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "CardUpdateResponse", namespace="http://www.samople.com/Prepaid")
public static class CardUpdateResponseWrapper {
#XmlElement(name="CARDUPDATE_RET")
private FVCardUpdateResponse response;
// Getter and setter for response
public static class FVCardUpdateResponse {
#XmlElement(name = "AccountNumber")
private String AccountNumber;
#XmlElement(name = "ResCode")
private String ResCode;
#XmlElement(name = "ResErrorCode")
private String ResErrorCode;
#XmlElement(name = "ResErrorMsg")
private String ResErrorMsg;
// Getters and setters
}
}
Now the CardUpdateResponseWrapper class will represent your root XML element and it will have instance of FVCardUpdateResponse which will represent the CARDUPDATE_RET XML element.
Do unmarshall it, just call:
File file = new File("D:/var/lib/tomcat7/webapps/tmpFiles/1.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(CardUpdateResponseWrapper.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
CardUpdateResponseWrapper wrapper = (CardUpdateResponseWrapper) jaxbUnmarshaller.unmarshal(file);
System.out.println(wrapper.getResponse().getResErrorMsg());
I think that the issue is a combination of two problems, one what Bohuslav is saying, the other you need to repeat your namespace on every XmlElement annotation, e.g.
#XmlElement(name = "ResCode", namespace="http://www.samople.com/Prepaid")
and one particular issue, you need to match the cases as well so the name for AccountNumber should be capitalized ACCOUNTNUMBER
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Item", propOrder = {
"code",
"name",
"price"
})
#XmlRootElement(name="inventory")
public class Item {
#XmlElement(name="catalog_num", required = true)
protected String code;
#XmlElement(name="catalog_descrip", required = true)
protected String name;
#XmlElement(name="prod_price")
protected double price;
public String getCode() {
return code;
}
JAXBContext databaseJC = JAXBContext.newInstance(Item.class);
Unmarshaller databaseUnmarshaller = databaseJC.createUnmarshaller();
File databaseXML = new File("src/forum6838882/database.xml");
Item item = (Item) databaseUnmarshaller.unmarshal(databaseXML);
My question is:
How could I get the #XmlElement(name="catalog_num", required = true) from item object. I need know the name="catalog_num" here.
JAXB (JSR-222) does not provide an API to introspect the metadata. You can however use the Java Reflection APIs (java.lang.reflect) to get the annotations and examine them yourself.
Demo
import java.lang.reflect.*;
import javax.xml.bind.annotation.*;
public class Demo {
public static void main(String[] args) throws Exception {
Field field = Item.class.getDeclaredField("code");
XmlElement xmlElement = field.getAnnotation(XmlElement.class);
System.out.println(xmlElement.name());
}
}
Output
catalog_num
In the root.class from my xi-schema, the element item and ohter objects are part of an itemList:
#XmlElementRef(name = "item", namespace = "xi", type = JAXBElement.class, required = false)
//...
protected List<Object> itemList;
I've in the ObjectFactory.class from the main-schema some items as JAXBElements like this:
#XmlElementDecl(namespace = "de-schema", name = "detailedInformation", substitutionHeadNamespace = "xi", substitutionHeadName = "item")
public JAXBElement<numItemType> createDetailedInformation(numItemType num) {
return new JAXBElement<numItemType>(_detailedInformation_QNAME, numItemType.class, null, num);
}
So the numItemType has some attributes and value(num) for the JAXBElement.
NumItemType.class:
#XmlJavaTypeAdapter(numItemTypeAdapter.class)
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "numItemType", namespace = "xi", propOrder = {
"num"
})
public class NumItemType {
#XmlValue
protected BigDecimal num;
#XmlAttribute(name = "precision")
protected String precision;
#XmlAttribute(name = "decimals")
protected String decimals;
//... more Attributes
}
But when JAXB unmarshal the XML document, it will has only elements, for example:
<detailedInformation>
<element1>1234</element1>
<element2>5678</element2>
<element3>bla</element3>
</detailedInformation>
When I marshal it, it should become (like the JAXB java code):
<detailedInformation element2="5678" element3="bla">1234</detailedInformation>
Therefore, I have written an numItemTypeAdapter.class with
NumItemTypeAdapter extends XmlAdapter
AdaptedNum.class:
public class AdaptedNum {
#XmlElement
private double element1;
#XmlElement
private String element2;
#XmlElement
private String element3;
/** Some getter/setter methods */
}
I thought, that would be help me http://blog.bdoughan.com/2012/02/xmlanyelement-and-xmladapter.html, but it is all a bit tricky :-/
It's a bit tricky to sort out exactly where your problem may be occurring. I'm assuming your original model was generated from an XML Schema, this should work as is without any modifications. I've attempted below to provide a scaled down version of your example which may help.
Root
package forum11343610;
import java.util.*;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElementRef(name = "item", namespace = "xi", type = JAXBElement.class, required = false)
protected List<Object> itemList = new ArrayList<Object>();
public List<Object> getItemList() {
return itemList;
}
public void setItemList(List<Object> itemList) {
this.itemList = itemList;
}
}
NumItemType
package forum11343610;
import java.math.BigDecimal;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "numItemType", namespace = "xi", propOrder = {
"num"
})
public class NumItemType {
#XmlValue
protected BigDecimal num;
#XmlAttribute(name = "precision")
protected String precision;
#XmlAttribute(name = "decimals")
protected String decimals;
//... more Attributes
}
ObjectFactory
package forum11343610;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
private static final QName _detailedInformation_QNAME = new QName("de-schema", "detailedInformation");
#XmlElementDecl(namespace = "xi", name = "item")
public JAXBElement<NumItemType> createItem(NumItemType num) {
return new JAXBElement<NumItemType>(_detailedInformation_QNAME, NumItemType.class, null, num);
}
#XmlElementDecl(namespace = "de-schema", name = "detailedInformation", substitutionHeadNamespace = "xi", substitutionHeadName = "item")
public JAXBElement<NumItemType> createDetailedInformation(NumItemType num) {
return new JAXBElement<NumItemType>(_detailedInformation_QNAME, NumItemType.class, null, num);
}
}
Demo
package forum11343610;
import java.math.BigDecimal;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class, ObjectFactory.class);
ObjectFactory objectFactory = new ObjectFactory();
Root root = new Root();
NumItemType numItemType = new NumItemType();
numItemType.num = BigDecimal.TEN;
numItemType.decimals = "1";
numItemType.precision = "2";
root.getItemList().add(objectFactory.createDetailedInformation(numItemType));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:ns2="de-schema" xmlns:ns3="xi">
<ns2:detailedInformation precision="2" decimals="1">10</ns2:detailedInformation>
</root>
Thank your for your comment.
That's the point:
Demo
NumItemType numItemType = new NumItemType();
numItemType.num = BigDecimal.TEN;
numItemType.decimals = "1";
numItemType.precision = "2";
root.getItemList().add(objectFactory.createDetailedInformation(numItemType));
It should unmarshal and map the XML automatically.
XML Input
<detailedInformation>
<element1>1234</element1>
<element2>5678</element2>
<element3>bla</element3>
</detailedInformation>
With the Code:
Demo
JAXBContext jc = JAXBContext.newInstance(Root.class, ObjectFactory.class);
Unmarshaller u = jc.createUnmarshaller();
File xml = new File("D:/", "test.xml");
Root root = (Root) u.unmarshal(xml);
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:ns2="de-schema" xmlns:ns3="xi">
<ns2:detailedInformation precision="2" decimals="1">10</ns2:detailedInformation>
</root>
I could parse the XML Document with DOM to a tree and marhalling with JAXB...
Thank you!
In other words:
I want to set new elements to the NumItemType.class for the unmarshalling without change the schema java code.
NumItemType.class
package forum11343610;
import java.math.BigDecimal;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "numItemType", namespace = "xi", propOrder = {
"num"
})
#XmlJavaTypeAdapter(NumItemTypeAdapter.class)
public class NumItemType {
#XmlValue
protected BigDecimal num;
#XmlAttribute(name = "precision")
protected String precision;
#XmlAttribute(name = "decimals")
protected String decimals;
//... more Attributes
}
NumItemTypeAdapter.class
public class NumItemTypeAdapter extends XmlAdapter<AdaptedNum, NumItemType> {
#Override
public NumItemType unmarshal(AdaptedNum an) throws Exception {
NumItemType nit = new NumItemType();
nit.setNum(an.getNum);
nit.setPrecision(an.getPrecision);
nit.setDecimals(an.getDecimals)
return nit;
}
}
AdaptedNum.class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "adaptedNum", namespace = "", propOrder = {
"element1",
"element2",
"element3"
})
public class AdaptedNum {
#XmlElement(name ="element1")
protected BigDecimal num;
#XmlElement(name ="element2")
private String decimals;
#XmlElement(name ="element3")
private String precison;
// set/get method
}