JAX-WS: XmlElementWrapper produces extra client code - java

I have some service with some WebMethod that returns object of Foo class:
public class Foo {
private List<Detail> detailList;
#XmlElement(name = "detail")
#XmlElementWrapper(name = "detailList")
public List<Detail> getDetailList() {
return detailList;
}
public void setDetailList(List<Detail> value) {
this.detailList = value;
}
public Foo() {
this.detailList = new ArrayList();
}
}
This code produces proper XML like:
<detailList>
<detail>
<key></key>
<value></value>
</detail>
<detail>
<key></key>
<value></value>
</detail>
<detailList/>
After building client JAR library it works OK.
But I really don't like the code I need to call to get the List:
foo.getDetailList().getDetail();
Because getDetailList() returns DetailList object.
How can I have getDetailList() method returning List without any changes in above XML?

#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Root", propOrder = {
"detailList"
})
public class Foo {
#XmlElementWrapper(name = "detailList", required = true)
#XmlElement(name = "detail")
private List<Detail> detailList;
public List<Detail> getDetailList() {
return detailList;
}
public void setDetailList(List<Detail> value) {
this.detailList = value;
}
public Foo() {
this.detailList = new ArrayList();
}
}

Related

JaxB: How Do I Retrieve Text Attribute from Nested Element?

I want the Country class to store the "ISO_3166-1_Alpha-2_Code" code. The code currently gets back the "ISO_3166-1_Numeric-3_Code" code. Can't figure out how to tweak the Country class to get the specific attribute I want.
XML:
<?xml version='1.0' encoding='UTF-8'?>
<wd:Message_Event_Configuration">
<wd:Message_Event_Configuration_Data>
<wd:Country_Reference wd:Descriptor="Saint Martin">
<wd:ID wd:type="WID">66b7082a21e510000961bb6d82b5002a</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-2_Code">MF</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-3_Code">MAF</wd:ID>
<wd:ID wd:type="ISO_3166-1_Numeric-3_Code">663</wd:ID>
</wd:Country_Reference>
<wd:Country_Reference wd:Descriptor="Saint Barthelemy">
<wd:ID wd:type="WID">881527f6cec910000ba81e8dccf61127</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-2_Code">BL</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-3_Code">BLM</wd:ID>
<wd:ID wd:type="ISO_3166-1_Numeric-3_Code">652</wd:ID>
</wd:Country_Reference>
</wd:Message_Event_Configuration_Data>
</wd:Message_Event_Configuration>
Country List:
#XmlRootElement(name = "Message_Event_Configuration")
#XmlAccessorType(XmlAccessType.FIELD)
public class Countries {
#XmlElementWrapper(name = "Message_Event_Configuration_Data")
#XmlElement(name = "Country_Reference")
private List<Country> countries = new ArrayList<Country>();
public List<Country> getCountries() {
return countries;
}
public void setCountries(List<Country> countries) {
this.countries = countries;
}
}
Country:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Country_Reference")
public class Country {
#XmlElement(name = "ID")
private String isoCode;
public Country() {
}
public Country(String isoCode) {
this.isoCode = isoCode;
}
#XmlAttribute(name = "ISO_3166-1_Alpha-2_Code")
public String getISOCode() {
return isoCode;
}
public void setISOCode(String isoCode) {
this.isoCode = isoCode;
}
}
The <Country_Reference> XML element contains the ISO codes in a rather
sophisticated way within several <wd:ID> XML elements.
It is therefore much too simple to model them as a Java String property.
Instead, you need to model the Java-structure with more similarity to the XML-structure.
The sequence of XML elements <wd:ID> can be modeled by a property List<ID> idList
which needs to be annotated by#XmlElement(name="ID") .
The XML attribute wd:Descriptor="...." can be modeled by a property String descriptor
which needs to be annotated by #XmlAttribute(name="Descriptor").
For your convenience you can add an all-arguments-constructor and some methods for getting
the WID and ISO codes from the List<ID>.
#XmlAccessorType(XmlAccessType.FIELD)
public class Country {
#XmlAttribute(name = "Descriptor")
private String descriptor;
#XmlElement(name = "ID")
private List<ID> idList;
public Country() {
}
public Country(String descriptor, String wid, String isoAlpha2Code, String isoAlpha3Code, String isoNumeric3Code) {
this.descriptor = descriptor;
idList = new ArrayList<>();
idList.add(new ID("WID", wid));
idList.add(new ID("ISO_3166-1_Alpha-2_Code", isoAlpha2Code));
idList.add(new ID("ISO_3166-1_Alpha-3_Code", isoAlpha3Code));
idList.add(new ID("ISO_3166-1_Numeric-3_Code", isoNumeric3Code));
}
public String getWid() {
return getIdByType("WID");
}
public String getIsoAlpha2Code() {
return getIdByType("ISO_3166-1_Alpha-2_Code");
}
public String getIsoAlpha3Code() {
return getIdByType("ISO_3166-1_Alpha-3_Code");
}
public String getIsoNumeric3Code() {
return getIdByType("ISO_3166-1_Numeric-3_Code");
}
private String getIdByType(String idType) {
for (ID id : idList) {
if (id.getType().equals(idType))
return id.getValue();
}
return null;
}
}
The XML elements <wd:ID> are quite complex. Therefore we need a separate POJO class for modeling them.
Let's call the class ID.
The XML text between <wd:ID ..> and </wd:ID> is modeled by the property String value
which needs to be annotated by #XmlValue.
The XML attribute wd:type="..." is modeled by the property String type
which needs to be annotated by #XmlAttribute.
For convenient use by the class Country above, an all-arguments-constructor is added.
#XmlAccessorType(XmlAccessType.FIELD)
public class ID {
#XmlAttribute
private String type;
#XmlValue
private String value;
public ID() {
}
public ID(String type, String value) {
this.type = type;
this.value = value;
}
// public getters and setters (omitted here fro brevity)
}
The screenshot below (taken from within the debugger) visualizes the Java structure
and confirms that the unmarshalling of your XML example works correctly:

java soap web service how to get a list from a xml request

I work with a SOAP web service. I want to recover the list productOrder from a java. My problem is that the list productOrder recovered from the java class is zero.
I dont't know how to recover this list from the java.
Below I explain my classes:
This is the request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="http://uciext.ws.hw3/wsdl">
<soapenv:Header/>
<soapenv:Body>
<wsdl:processOrder>
<!--Optional:-->
<arg0>
<vendorCode>330029</vendorCode>
<vendorName>My Shop</vendorName>
<orderNumber>1000339</orderNumber>
<!--1 or more repetitions:-->
<wsdl:productOrder>
<!--Optional:-->
<productSku>111</productSku>
<!--Optional:-->
<productName>Kindle Fire</productName>
<!--Optional:-->
<orderQuantity>5</orderQuantity>
</wsdl:productOrder>
</arg0>
</wsdl:processOrder>
</soapenv:Body>
</soapenv:Envelope>
I have the Order.java to parse this xml
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"vendorCode",
"vendorName",
"orderNumber",
"productOrder"
})
#XmlRootElement(name = "order")
public class Order {
#XmlElement(required = true)
protected String vendorCode;
#XmlElement(required = true)
protected String vendorName;
#XmlElement(required = true)
protected String orderNumber;
#XmlElement(required = true)
protected List<ProductOrder> productOrder;
public String getVendorCode() {
return vendorCode;
}
public void setVendorCode(String value) {
this.vendorCode = value;
}
public String getVendorName() {
return vendorName;
}
public void setVendorName(String value) {
this.vendorName = value;
}
public String getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(String value) {
this.orderNumber = value;
}
public List<ProductOrder> getProductOrder() {
if (productOrder == null) {
productOrder = new ArrayList<ProductOrder>();
}
return this.productOrder;
}
}
I have a method processOrder in a interface and their implementation. This
is the interface
#WebService(targetNamespace = "http://uciext.ws.hw3/wsdl")
public interface OrderServiceWS {
#WebMethod
OrderConfirm processOrder(#WebParam(name="arg0", mode=Mode.IN) Order order) throws Exception;
}
processOrder is in the class
#XmlRootElement(name = "processOrder", namespace = "http://uciext.ws.hw3/wsdl")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "processOrder", namespace = "http://uciext.ws.hw3/wsdl")
public class ProcessOrder {
#XmlElement(name = "arg0", namespace = "")
private com.uciext.ws.hw3.service.model.order.Order arg0;
public com.uciext.ws.hw3.service.model.order.Order getArg0() {
return this.arg0;
}
public void setArg0(com.uciext.ws.hw3.service.model.order.Order arg0) {
this.arg0 = arg0;
}
}
This is the interface implementation where is called the method processOrder.
#WebService(
portName = "OrderPort",
serviceName = "OrderService",
targetNamespace = "http://uciext.ws.hw3/wsdl",
endpointInterface = "com.uciext.ws.hw3.service.OrderServiceWS")
public class OrderServiceWSImpl implements OrderServiceWS {
private InventoryManagerImpl manager = new InventoryManagerImpl();
#Override
public OrderConfirm processOrder(Order order) throws Exception {
Util.log("SOAP processOrder request [ order=" + order);
Util.log("OrderServiceWS start processOrder");
OrderDAO orderDAO = new OrderDAO();
List<ProductOrder> productOrderList = new ArrayList<ProductOrder>();
List<ProductOrderDAO> productOrderDAOList = new ArrayList<ProductOrderDAO>();
ProductOrderDAO productOrderDAO1 = null;
ProductDAO productDAO = null;
OrderConfirm orderConfirm = null;
ProductOrder productOrder = null;
ProductConfirm productConfirm = null;
List<ProductConfirm> productConfirmList = new ArrayList<ProductConfirm>();
Double totalPrice = 0.0;
Util.log("OrderServiceWS start processOrder vendorCode "+order.getVendorCode());
orderDAO.setVendorCode(order.getVendorCode());
orderDAO.setVendorName(order.getVendorName());
orderDAO.setOrderId(Long.parseLong(order.getOrderNumber()));
Util.log("OrderServiceWS processOrder before productOrderList ");
productOrderList = order.getProductOrder();
Util.log("OrderServiceWS processOrder before productOrderList size "+productOrderList.size());
The value of vendorCode is right but productOrderList.size() is cero

Marshalling Java object to XML with JAXB returns unexpected output

I am having issues marshalling a bean into XML using JAXB. I have multiple REST API endpoints and I want to return a uniform response from all the endpoints, like the following:
<response>
<responseHeader> <!-- this will be same for all the end points -->
<status>OK</status>
<stausCode>AB-123<statusCode>
</responseHeader>
<responseBody>
<!-- contains end point specific data, could be differnet-->
</responseBody>
</response>
So what I did is created a generic response DTO:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "response")
public class GenericResponseDto implements Serializable {
#XmlElement(name="responseHeader")
private GenericResponseHeaderDto responseHeader;
#XmlAnyElement(name="responseBody")
private Object responseBody;
public GenericResponseHeaderDto getResponseHeader() {
return responseHeader;
}
public void setResponseHeader(GenericResponseHeaderDto responseHeader) {
this.responseHeader = responseHeader;
}
public Object getResponseBody() {
return responseBody;
}
public void setResponseBody(Object responseBody) {
this.responseBody = responseBody;
}
}
Where the response body field will be replaced by the following object for one of the endpoint responses:
#XmlRootElement(name = "responseBody")
#XmlAccessorType(XmlAccessType.FIELD)
public class Person implements Serializable {
#XmlElement(required = false)
private String phoneNumber;
#XmlElement(required = false)
private Integer personId;
public Integer getPersonId() {
return personId;
}
public void setPersonId(Integer personId) {
this.personId = personId;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
#Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
Here is my Jersey endpoint:
#POST
#Path("/myAPIFirstEndPoint")
#Produces({MediaType.APPLICATION_XML})
public GenericResponseDto myAPIFirstEndPoint(ABC abc) {
// some work and getting person dto
Person person = someWork.doWork();
GenericResponseDto genericResponseDto = new GenericResponseDto();
// not setting any responseHeader for now, so ignore
genericResponseDto.setResponseBody(row);
return genericResponseDto;
}
But it's not working as expected. The toString() method is being called on the Person object, instead of it being marshalled to XML. I'm getting the following incorrect response:
<?xml version="1.0" encoding="UTF-8" ?>
<response>
<responseBody>
path.to.package.Person#36af3690[phoneNumber=+123456789,personId=-1]
</responseBody>
</response>
Can you please tell me what I'm doing wrong? I am using Jersey and JAXB with Spring.
EDIT:
Introduced generics:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "response")
#XmlSeeAlso({Person.class})
public class GenericResponseDto<T> implements Serializable {
#XmlElement(name="responseHeader")
private GenericResponseHeaderDto responseHeader;
#XmlElement(name="responseBody")
private T responseBody;
public GenericResponseHeaderDto getResponseHeader() {
return responseHeader;
}
public void setResponseHeader(GenericResponseHeaderDto responseHeader) {
this.responseHeader = responseHeader;
}
public T getResponseBody() {
return responseBody;
}
public void setResponseBody(T responseBody) {
this.responseBody = responseBody;
}
}
changed Jersey endpoint as follows:
#POST
#Path("/myAPIFirstEndPoint")
#Produces({MediaType.APPLICATION_XML})
public GenericResponseDto<Person> myAPIFirstEndPoint(ABC abc) {
// some work and getting person dto
Person person = someWork.doWork();
GenericResponseDto<Person> genericResponseDto = new GenericResponseDto<Person>();
// not setting any responseHeader for now, so ignore
genericResponseDto.setResponseBody(row);
return genericResponseDto;
}
Still getting the same response as mentioned above.
Now getting this response, after adding #XmlSeeAlso({Person.class}) in GenericResponseDto
<?xml version="1.0" encoding="UTF-8" ?>
<response>
<responseBody xsi:type="Person">
<phoneNumber>+923454502dd0559</phoneNumber>
<personId>-1</personId>
<token />
</responseBody>
</response>

JAXB unmarshal XML elements to object wrapper

I am trying unmarshal XML to object with wrapped elements.
XML looks:
<?xml version="1.0" encding="utf-8"?>
<Output xmlns="_xxx_" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Companies>
<Company>
...
</Company>
<Company>
...
</Company>
...
</Companies>
<People>
<Person>
...
</Person>
<Person>
...
</Person>
...
</People>
<Relations>
<Relation>
...
</Relation>
<Relation>
...
</Relation>
...
</Relations>
<Info ... />
</Output>
I would like to have Compenies, People and Relations in Lists. I try this code, but it doesn't work:
Java Code
Ouput
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Output", namespace = "_xxx_")
#XmlType(propOrder={"companies", "people", "relations", "info"})
public class Output {
#XmlElementWrapper(name = "Companies")
#XmlElement(name = "Company" type = Company.class)
private List<Company> companies;
#XmlElementWrapper(name = "People")
#XmlElement(name = "Person", type = Person.class)
private List<Person> people;
#XmlElementWrapper(name = "Relations")
#XmlElement(name = "Relation", type = Relation.class)
private List<Relation> relations;
#XmlElement(name = "Info")
private Info info;
public Output() {
this.companies = new ArrayList<>();
this.people = new ArrayList<>();
this.relations = new ArrayList<>();
}
public List<Company> getCompanies() {
return companies;
}
public void setCompanies(List<Company> companies) {
this.companies = companies;
}
public List<Person> getPeople() {
return people;
}
public void setPeople(List<Person> people) {
this.people = people;
}
public List<Relation> getRelations() {
return relations;
}
public void setRelations(List<Relation> relations) {
this.relations = relations;
}
public Info getInfo() {
return info;
}
public void setInfo(Info info) {
this.info = info;
}
}
And for example Company looks (other classes are similar)
#XmlRootElement(name = "Company")
public class Company {
...
}
If I use this it creates empty Lists, but, if I change code to:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Output", namespace = "_xxx_")
#XmlType(propOrder={"companies", "people", "relations", "info"})
public class Output {
#XmlElement(name = "Companies")
private Companies companies;
#XmlElement(name = "People")
private People people;
....
public Companies getCompanies() {
return companies;
}
public void setCompanies(Companies companies) {
this.companies = companies;
}
...
}
where Companies class looks:
#XmlRootElement(name = "Companies")
#XmlAccessorType (XmlAccessType.FIELD)
public class Companies {
#XmlElement(name = "Company")
private List<Company> companies;
public List<Company> getCompanies() {
return companies;
}
public void setCompanies(List<Company> companies) {
this.companies = companies;
}
}
it works fine, but it isn't nice solution.
Can anyone advise me what is wrong here? Thanks :)
I solved my problem by using the instructions on this link. I use mapping to java.util.Map.

Java Unmarshal list of objects with a class wrapper with JAXB

From an XQuery performed by BaseX server I get a result like that:
<ProtocolloList>
<protocollo>
<numero>1</numero>
<data>2014-06-23</data>
<oggetto/>
<destinatario/>
<operatore/>
</protocollo>
...
</ProtocolloList>
And I need to convert this result in a List of Protocollo objects with JAXB so that I can show them with JList. Thus, following one of the discussions here I've declared the following classes:
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "protocollo")
public class Protocollo {
private int numero;
private String data;
private String oggetto;
private String destinatario;
private String operatore;
public Protocollo(String d, String o, String des, String op) {
this.data = d;
this.oggetto = o;
this.destinatario = des;
this.operatore = op;
}
public Protocollo() {
}
#XmlElement
public int getNumero() {
return numero;
}
public void setNumero(int numero) {
this.numero = numero;
}
#XmlElement
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
#XmlElement
public String getOggetto() {
return oggetto;
}
public void setOggetto(String oggetto) {
this.oggetto = oggetto;
}
#XmlElement
public String getDestinatario() {
return destinatario;
}
public void setDestinatario(String destinatario) {
this.destinatario = destinatario;
}
#XmlElement
public String getOperatore() {
return operatore;
}
public void setOperatore(String operatore) {
this.operatore = operatore;
}
}
and
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "ProtocolloList")
public class ProtocolloList {
#XmlElementWrapper(name = "ProtocolloList")
#XmlElement(name = "protocollo")
private ArrayList<Protocollo> ProtocolloList;
public ArrayList<Protocollo> getProtocolloList() {
return ProtocolloList;
}
public void setProtocolloList(ArrayList<Protocollo> protocolloList) {
ProtocolloList = protocolloList;
}
}
and finally I execute the converion like that:
JAXBContext jaxbContext = JAXBContext.newInstance(Protocollo.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(this.resultXML);
protocolli = (ProtocolloList) unmarshaller.unmarshal(reader);
And I keep on getting this exception:
unexpected element (uri:"", local:"ProtocolloList"). Expected elements are <{}protocollo>
I suppose I'm making some mistakes with annotations.
Can you help?
For your use case you do not need the #XmlElementWrapper annotation. This is because the ProtocolList element corresponds to your #XmlRootElement annotation. Then you need the #XmlElement annotation on the property to grab each of the list items.
#XmlRootElement(name = "ProtocolloList")
public class ProtocolloList {
private ArrayList<Protocollo> ProtocolloList;
#XmlElement(name = "protocollo")
public ArrayList<Protocollo> getProtocolloList() {
return ProtocolloList;
}
}
Note:
By default you should annotate the property. If you want to annotate the fields you should put #XmlAccessorType(XmlAccessType.FIELD) on your class.
UPDATE
You need to make sure your JAXBContext is aware of the root class. You can change your JAXBContext creation code to be the following:
JAXBContext jaxbContext = JAXBContext.newInstance(ProtocolloList.class);

Categories

Resources