JAX-WS and SOAP mapping for response - java

I have to implements a webservice interface and the following response SOAP message has been provided:
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body SOAP-ENC:encodingStyle="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:NS2="urn:WebserviceIntf">
<NS1:VoidPaymentResponse xmlns:NS1="urn:WebserviceIntf-Webservice">
<return href="#1"/>
</NS1:VoidPaymentResponse>
<NS2:TVoidPaymentResponse id="1" xsi:type="NS2:TVoidPaymentResponse">
<MessageCode xsi:type="xsd:string">00</MessageCode>
<MessageDescription xsi:type="xsd:string">Successful</MessageDescription>
</NS2:TVoidPaymentResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
This is what i have for the interface web method.
#WebMethod(operationName = "VoidPayment")
#ResponseWrapper(targetNamespace = "urn:WebserviceIntf-Webservice")
#RequestWrapper(targetNamespace = "urn:WebserviceIntf-Webservice")
public VoidPaymentDetailResponse voidPayment(
#WebParam(name = "LoginID") String loginId,
#WebParam(name = "Password") String password,
#WebParam(name = "TransactionID") String transactionId,
#WebParam(name = "Echo") String additionalInformation,
#WebParam(name = "TVoidPaymentResponse", mode = WebParam.Mode.INOUT, targetNamespace = "urn:WebserviceIntf") Holder<VoidPaymentDetail> voidPaymentDetails) {
VoidPayment vp = new VoidPayment(loginId, password, transactionId, additionalInformation);
VoidPaymentDetail vpd = voidPayment(vp);
voidPaymentDetails.value = vpd;
return new VoidPaymentDetailResponse("#" + vpd.getId());
}
When i test my web method on SOAPUI. I get the following soap message:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns3:VoidPaymentResponse xmlns:ns2="http://payapi.afrocoin.com/" xmlns:ns3="urn:WebserviceIntf-Webservice" xmlns:ns4="urn:WebserviceIntf">
<return href="#Od4dY"/>
<ns4:TVoidPaymentResponse id="Od4dY">
<MessageCode>32</MessageCode>
<MessageDescription>Login failed</MessageDescription>
</ns4:TVoidPaymentResponse>
</ns3:VoidPaymentResponse>
</soap:Body>
</soap:Envelope>
Clearly there is a difference between the two messages, and i need to comply fully with the expected response.
What annotations do i need to add to the web method to make sure that this happens.
I have spent countless hours trying to figure this out.
Additional codes:
#XmlAccessorType(XmlAccessType.FIELD)
public class VoidPaymentDetailResponse {
#XmlAttribute(name = "href")
private String paymentDetail;
public VoidPaymentDetailResponse() {
}
public VoidPaymentDetailResponse(String paymentDetail) {
this.paymentDetail = paymentDetail;
}
public String getPaymentDetail() {
return paymentDetail;
}
public void setPaymentDetail(String paymentDetail) {
this.paymentDetail = paymentDetail;
}
}
#XmlType(name = "TVoidPaymentResponse", namespace = "urn:WebserviceIntf")
#Entity
#Table(name = "VoidPaymentResponseDetails")
#XmlAccessorType(XmlAccessType.FIELD)
public class VoidPaymentDetail implements Serializable {
private static final long serialVersionUID = 19383656L;
#Id
#XmlTransient
private Long detailId = IdGenerator.generateId();
#XmlAttribute
#XmlID
#Transient
private String id = IdGenerator.generateIdentifier(5);
#XmlElement(name = "MessageCode")
private String messageCode;
#XmlElement(name = "MessageDescription")
private String messageDescription;
#XmlTransient
#OneToOne
private VoidPayment voidPaymentRequest;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public Long getDetailId() {
return detailId;
}
public void setDetailId(Long detailId) {
this.detailId = detailId;
}
public VoidPayment getVoidPaymentRequest() {
return voidPaymentRequest;
}
public void setVoidPaymentRequest(VoidPayment voidPaymentRequest) {
this.voidPaymentRequest = voidPaymentRequest;
}
public String getMessageCode() {
return messageCode;
}
public void setMessageCode(String messageCode) {
this.messageCode = messageCode;
}
public String getMessageDescription() {
return messageDescription;
}
public void setMessageDescription(String messageDescription) {
this.messageDescription = messageDescription;
}
}
I am using the default jboss wsimport.
Thanks

As per my comment. I don't believe that you can replicate the first message. This is because it contains two separate part's in the body.
http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383533
7.1 RPC and SOAP Body
A method response is modelled as a struct.
The method response is viewed as a single struct containing an accessor for the return value and each [out] or [in/out] parameter. The first accessor is the return value followed by the parameters in the same order as in the method signature.

Related

JAXB Unmarshall XML with same tag name but different structure

I'm trying to unmarshall this kind of XML:
<result>
<avance>0.0000</avance>
<operation_status>0</operation_status>
<service>Bank Account</service>
<service>
<min_amount>1.00</min_amount>
<max_amount>1499.00</max_amount>
<currency>USD</currency>
</service>
</result>
I have created this kind of class structure:
public class Result {
private BigDecimal avance;
private Integer operationStatus;
private String serviceDesc;
private Service service;
#XmlElement(name = "service")
public String getServiceDesc() {
return serviceDesc;
}
public void setServiceDesc(String serviceDesc) {
this.serviceDesc = serviceDesc;
}
#XmlElement(name = "service")
public Service getService() {
return service;
}
public void setService(Service service) {
this.service = service;
}
#XmlElement(name = "avance")
public BigDecimal getAvance() {
return avance;
}
public void setAvance(BigDecimal avance) {
this.avance = avance;
}
#XmlElement(name = "operation_status")
public Integer getOperationStatus() {
return operationStatus;
}
public void setOperationStatus(Integer operationStatus) {
this.operationStatus = operationStatus;
}
}
and Service looks like this:
#XmlRootElement
public class Service {
private BigDecimal minAmount;
private BigDecimal maxAmount;
private String currency;
#XmlElement(name = "min_amount")
public BigDecimal getMinAmount() {
return minAmount;
}
public void setMinAmount(BigDecimal minAmount) {
this.minAmount = minAmount;
}
#XmlElement(name = "max_amount")
public BigDecimal getMaxAmount() {
return maxAmount;
}
public void setMaxAmount(BigDecimal maxAmount) {
this.maxAmount = maxAmount;
}
#XmlElement(name = "currency")
public String getCurrency() {
return currency;
}
public void setCurrency(String currency) {
this.currency = currency;
}
}
When I receive response from some external service I'm able to unmarshal that string and result has correct Service class set but serviceDesc is always null. Is there any way how I can unmarshall this structure correctly?
In another question (for which this question is marked as duplicate) case is when you have same tag name but different number of attributes in my case content of the tag is different, also in that question classes to which content should be unmarshaled are child and parent in my case one is String another one some custom object. Think that's why I wasn't able to implement XmlAdapter properly.
Your XML structure is not valid.
You shouldn't have the same tag twice for different structure in the same namespace.
Solutions :
Rename your tag (serviceDesc for example)
Use 2 different namespaces ( ns1:service and ns2:service for example)

XML to ArrayList Java Rest Application

This is my first project with JEE/Rest applications
My problem is that I just can't get my server to parse an xml post to my object wich contains an ArrayList...
Here is where I handle the #POST, the server just returns what it gets:
Search.java
#POST
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Request returnRequest(Request input) {
return input;
}
When I post this:
<request>
<name>Salle 101</name>
<equipments>
<equipment>
<rfName>Tableau Blanc</rfName>
</equipment>
<equipment>
<rfName>Video Projecteur</rfName>
</equipment>
</equipments>
<startslot>9</startslot>
<endslot>10</endslot>
<size>6</size>
<site>Toulouse</site>
</request>
I get this:
<?xml version="1.0" encoding="UTF-8"?>
<request>
<endslot>10</endslot>
<equipments/>
<name>Salle 101</name>
<site>Toulouse</site>
<size>6</size>
<startslot>9</startslot>
</request>
As you can see I don't have my equipment list correctly returned...
Here are my entity files:
Request.java
#XmlRootElement(name = "request")
public class Request {
//#XmlElement
private String name;
//#XmlElement
private Equipments equipments;
//#XmlElement
private int startSlot;
//#XmlElement
private int endSlot;
//#XmlElement
private int size;
//#XmlElement
private String site;
public Request(){
// my stuff
}
// getters & setters
Equipment.java
#Entity
#Table(name = "equipment")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Equipment.findAll", query = "SELECT e FROM Equipment e"),
#NamedQuery(name = "Equipment.findByRfName", query = "SELECT e FROM Equipment e WHERE e.rfName = :rfName")})
public class Equipment implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 20)
#Column(name = "RF_NAME")
private String rfName;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "equipment")
private Collection<RoomEquipment> roomEquipmentCollection;
public Equipment() {
}
public Equipment(String rfName) {
this.rfName = rfName;
}
//...
After a little bit of searching I saw that creating a class for the ArrayList could be useful, so here it is:
Equipments.java
#XmlRootElement(name="equipments")
public class Equipments implements Serializable {
private ArrayList<Equipment> equipments;
public Equipments(){
this.equipments = new ArrayList<>();
}
public void add(Equipment eq){
this.getEquipments().add(eq);
}
public ArrayList<Equipment> getEquipments(){
return this.equipments;
}
/**
* #param equipments the equipments to set
*/
public void setEquipments(ArrayList<Equipment> equipments) {
this.equipments = equipments;
}
}
Thanks for your help
I finally found how do deal with it. It's just that I didn't really understood how #annotations were working...
This website helped me to figure it out: http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html

How can I encourage JAXB to read XmlElement content string?

I'm not getting my "announcements" when I try to parse messages like these:
<?xml version="1.0" encoding="UTF-8"?>
<message messageType="SUBSCRIBER" messageName="ANNOUNCEMENT">
<announcement time="12:30">
Lunchtime!
</announcement>
<announcement time="32:00">
Good night ...
</announcement>
<errorText>Phone number missing, subscriber: Dick</errorText>
</message>
The JAXB Java class seemed pretty straightforward. And is based on similar messages that work. In this case the difference seems to be that I can have two different types of nested element (XmlElement) in the main block.
The code below actually parses the XML but it doesn't call the Announcement.setMessageText() method. Whereas I can see that the time attribute is set from the XML and I have an array size=2 for announcements, it reads and sets the single errorText XmlElement. Btw, I also removed the tag from the code and XML -- Not change in treatment of my messageText. Ideas welcome!
#XmlRootElement(name = "message")
public class AnnouncementMessage
{
#XmlAttribute
public String getMessageName(){
return this.messageName;
}
public void setMessageName( String name ){
this.messageName = name;
}
#XmlAttribute
public String getMessageType(){
return messageType;
}
public void setMessageType( String newMessageType ){
this.messageType = newMessageType;
}
#XmlElement(name = "errorText")
public String getErrorText(){
return errorText;
}
public void setErrorText( String newMsg ){
this.errorText = newMsg;
}
private List<Announcement> announcements = new ArrayList<>();
#XmlElement(name = "announcement")
public List<Announcement> getAnnouncements(){
return this.announcements;
}
public void setAnnouncements( List<Announcement> newAnnouncements ){
this.announcements = newAnnouncements;
}
}
And the Announcement class:
#XmlRootElement(name = "announcement")
public class Announcement
{
private String messageText = "";
private String time = "12:00";
XmlAttribute(name ="time")
public String getTime(){
return this.time;
}
public void setTime( String newMsg ){
this.messageText = time;
}
#XmlElement(name="announcement")
public String getMessageText(){
return this.messageText;
}
public void setMessageText( String newMsg ){
this.messageText = newMsg;
}
Announcement(){
}
}
Using the name parameter in the for the XmlElement seem to make no difference. My thanks in advance.
Use the #XmlValue annotation for the message text:
#XmlValue
public String getMessageText(){
return this.messageText;
}
Additionally, as noted in this related answer by OP, an #XmlAccessorType(XmlAccessType.NONE) is needed on the Announcement class if it contains getter/setter pairs that are not mapped to/from XML and don't have an #XmlTransient annotation.

XML to java Object conversion using simpleframework

I am converting xml file to java object using simpleframework. I am using generics since my xml's internal nodes gets changing. But simpleframework throws an error while converting. Here is my sample code:
#Root(name = "searchresult", strict = false)
public class ResponseVO<T>
{
#ElementList(entry = "document", inline = true)
public List<T> elementVOList = new ArrayList<T>();
public List<T> getElementsVOList()
{
return elementVOList;
}
public void setElementsVOList(List<T>list)
{
elementVOList = list;
}
}
ResponseVO is container for various other VOs as below:
#Root(name = "document", strict = false)
public class Projects_Display_VO
{
#Element(name = "projectname")
private String projectName;
#Attribute(name = "id")
private int tmpid;
public int getTmpid()
{
return tmpid;
}
public void setTmpid(int tmpid)
{
this.tmpid = tmpid;
}
/**
* ProjectId
*/
#Element(name = "projectid")
private String projectID;
public String getProjectName()
{
return projectName;
}
public void setProjectName(String projectName)
{
this.projectName = projectName;
}
public int getProjectID()
{
return Integer.parseInt(projectID);
}
public void setProjectID(String projectID)
{
this.projectID = projectID;
}
}
And the XML file is as below:
<searchresult>
<query>id:(PROJ2 PROJ6)</query>
<document id="0">
<projectid>2</projectid>
<projectname>Redundant Demo Project</projectname>
<doctype>Projects</doctype>
<summary>||Demo Project</summary>
<title>Redundant Demo Project</title>
</document>
<document id="1">
<projectid>6</projectid>
<projectname>Redundant Demo Project2</projectname>
<doctype>Projects</doctype>
<summary>||Main terminal links.</summary>
<title>Terminal 5 Project</title>
</document>
</searchresult>
The code for conversion is as bellow:
ResponseVO<Projects_Display_VO> resp = (ResponseVO<Projects_Display_VO>) SerializationUtil.deserialize(ResponseVO.class, reader);
Here i am using serializer from simpleframework. But it throws following error
Exception::Attribute 'id' does not have a match in class java.lang.Object at line 5
org.simpleframework.xml.core.AttributeException: Attribute 'id' does not have a match in class java.lang.Object at line 5
at org.simpleframework.xml.core.Composite.readAttribute(Composite.java:555)
at org.simpleframework.xml.core.Composite.readAttributes(Composite.java:474)
at org.simpleframework.xml.core.Composite.readSection(Composite.java:387)
I dont know whats going wrong here.A it works fine without generics.
Thanks in advance
This is caused by erasure, T is not available at runtime. Java does not allow it.

Deserialization of xml with simplexml in java

I'm trying to deserialize an xml string with SimpleXML, i've looked at their examples but i'm not really sure that wether i grasp the concept or not.
Sample XML (validates):
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<Response xmlns="http://localhost/webservices/">
<Result>
<Item><ID>0</ID><language /><price>168</price></Item>
<Item><ID>1</ID><language /><price>178</price></Item>
<Item><ID>2</ID><language /><price>195</price></Item>
<Item><ID>3</ID><language /><price>169</price></Item>
<Item><ID>4</ID><language /><price>178</price></Item>
<Item><ID>5</ID><language /><price>178</price></Item>
<Item><ID>6</ID><language /><price>149</price></Item>
<Item><ID>7</ID><language /><price>178</price></Item>
<Item><ID>8</ID><language /><price>168</price></Item>
<Item><ID>9</ID><language /><price>179</price></Item>
<Item><ID>10</ID><language /><price>147</price></Item>
<Item><ID>11</ID><language /><price>165</price></Item>
<Item><ID>12</ID><language /><price>192</price></Item>
<Item><ID>13</ID><language /><price>218</price></Item>
<Item><ID>14</ID><language /><price>144</price></Item>
<Item><ID>15</ID><language /><price>141</price></Item>
</Result>
</Response>
</soap:Body></soap:Envelope>
And the java code:
#Root(name="Result",strict=false)
public class ItemList {
#ElementList(entry="Item")
private List<Item> _list;
public List<Book> GetItemList()
{
return _list;
}
public void SetItemList(List<Item> value)
{
this._list = value;
}
}
#Root(strict=false)
public class Item {
#Element(name="ID")
private String _ID;
#Element(name="price")
private String _price;
public String GetPrice()
{
return _price;
}
public void SetPrice(String value)
{
this._price = value;
}
public String GetID()
{
return _ID;
}
public void SetID(String value)
{
this._ID = value;
}
public Item(String ID,
String price)
{
this._ID = ID;
this._price = price;
}
}
Any help is appreciated.
I have a suggestion, but it's not ready to run (see below). However, ther maybe another, better solution ...
Class Item
Holding all your informations.
#Root(name="Item")
public class Item
{
#Element(name="ID", required=true)
private int id;
#Element(name="language", required=true)
private String language;
#Element(name="price", required=true)
private int price;
// ...
}
Class Result
Constructing everything around Item. Btw. you dont have to use inner classes here.
#Namespace(prefix="soap", reference="http://schemas.xmlsoap.org/soap/envelope/")
#Root(name="Envelope")
public class Result
{
#Namespace(prefix="soap")
#Element(name="Body")
private SoapBody body;
// ...
// -----------------------------------------------------------------
// -- Some inner classes, constructing the elements as in you xml --
// -----------------------------------------------------------------
#Namespace(prefix="soap")
#Root(name="Body")
static class SoapBody
{
#Element(name="Response")
private Response response;
// ...
}
#Root(name="Response")
static class Response
{
#ElementList(name="Result", required=true)
private List<Item> result;
// ...
}
}
(Example) How to use this code
Writing
File f = ...
Serializer ser = new Persister();
Result r = new Result();
ser.write(r, f);
Reading
File f = ...
Serializer ser = new Persister();
Result r = ser.read(Result.class, f);
Now ... there's one problem which prevents this example from running: <language />
This empty Element let SimpleXML throw a ValueRequiredException.
#Element( required=false)
private String language;
Add this in your Item class and generate the getter and setter.I think it should work

Categories