JAXB - Mapping SOAP to Java Classes - soap

I need help to mapping my Soap Envelope to java Classes, my intention manipulate the results to DB.
I dont't have problems with get my SOAP Envelope or to work With DB, my problems is totaly with JABX and mapping my classes according my SOPA Envoloap.
This is my SOAP:
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<KD4SoapHeaderV2 xmlns="http://www.ibm.com/KD4Soap">A03ODA1YzhlZDQ2MWQAAQ==</KD4SoapHeaderV2>
</soap:Header>
<soap:Body>
<Response xmlns="http://tempuri.org/">
<Result xmlns:a="http://schemas.datacontract.org/2004/07/PS.SharedWebServices.Response" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Errors />
<a:Count>329</a:Count>
<a:Return>SUCCESS</a:Return>
<a:DashboardDTOs>
<a:DashboardDTOs>
<a:Value>28.58</a:Value>
<a:Code>O001</a:Code>
<a:Name>Test2</a:Name>
</a:DashboardDTOs>
<a:DashboardDTOs>
<a:Value>40.22</a:Value>
<a:Code>O002</a:Code>
<a:Name>Test2</a:Name>
</a:DashboardDTOs>
<a:DashboardDTOs>
<a:Value>54.11</a:Value>
<a:Code>O003</a:Code>
<a:Name>Test3</a:Name>
</a:DashboardDTOs>
</a:DashboardDTOs>
</Result>
</Response>
</soap:Body>
</soap:Envelope>
This is my the Class with receive the main values (count, return and list of Dashboards DTO):
#XmlRootElement(name = "Response")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Response", propOrder = { "Count", "Return", "DashboardDTOs"})
public class Result {
#XmlElement(name="Count", required = true)
private Integer Count;
#XmlElement(name="Return", required = true)
private String Return;
#XmlElement(name = "DashboardDTOs")
private List<DashboardDTOs> DashboardDTOs;
...
This is the second model that receives the DashboardDTO:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "DashboardDTOs", propOrder = {
"Value",
"Code",
"Name"
})
public class DashboardDTOs {
#XmlElement(name = "Value")
private double Value;
#XmlElement(name = "Code")
private String Code;
#XmlElement(name = "Name")
private String Name;
...
And my app try to convert the SOAPEnvelope to a Result but I got error:
Unmarshaller unmarshaller = JAXBContext.newInstance(Result.class).createUnmarshaller();
GetListSummarizedTransactionResultDTO returnValue = (Result)unmarshaller.unmarshal(soapMessagem.getSOAPBody().extractContentAsDocument());
unexpected element (uri:"http://tempuri.org/", local:"Response"). Expected elements are <{}Result>
what I'm doing wrong?
Thans

Try this:
First you have your root class, Response
#XmlRootElement(name = "Response", namespace = "http://tempuri.org/")
#XmlAccessorType(XmlAccessType.FIELD)
public class Response {
#XmlElement(name="Result", namespace = "http://tempuri.org/")
private Result result;
}
which contains the Result:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Result", propOrder = { "Errors", "Count", "Return", "DashboardDTOs"})
public class Result {
#XmlElement(name="Errors", required = true, namespace = "http://schemas.datacontract.org/2004/07/PS.SharedWebServices.Response")
private String Errors;
#XmlElement(name="Count", required = true, namespace = "http://schemas.datacontract.org/2004/07/PS.SharedWebServices.Response")
private Integer Count;
#XmlElement(name="Return", required = true, namespace = "http://schemas.datacontract.org/2004/07/PS.SharedWebServices.Response")
private String Return;
#XmlElement(name = "DashboardDTOs", namespace = "http://schemas.datacontract.org/2004/07/PS.SharedWebServices.Response")
private List<DashboardDTOs> DashboardDTOs;
}
Now it get's a bit "messy" for my taste. Your xml contains an element DashboardDTOs which inside it contains a list of DashboardDTOs and those have value, code, names. So you need to create a DashboardDTOs class like this:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "DashboardDTOs", propOrder = {
"Value",
"Code",
"Name",
"DashboardDTOs"
})
public class DashboardDTOs {
#XmlElement(name = "Value", namespace = "http://schemas.datacontract.org/2004/07/PS.SharedWebServices.Response")
private double Value;
#XmlElement(name = "Code", namespace = "http://schemas.datacontract.org/2004/07/PS.SharedWebServices.Response")
private String Code;
#XmlElement(name = "Name", namespace = "http://schemas.datacontract.org/2004/07/PS.SharedWebServices.Response")
private String Name;
#XmlElement(name = "DashboardDTOs", namespace = "http://schemas.datacontract.org/2004/07/PS.SharedWebServices.Response")
private List<DashboardDTOs> DashboardDTOs;
}
These POJOs will allow you to marshal/unmarshal with the specified xml inside the body.
Update to respond to comment:
With the updated xml, the classes would look like:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "DashboardDTOs")
public class DashboardDTOs {
#XmlElement(name = "DashboardDTO", namespace = "http://schemas.datacontract.org/2004/07/PS.SharedWebServices.Response")
private List<DashboardDTO> dashboardDTO;
}
and:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(propOrder = {
"value",
"code",
"Nnme",
})
public class DashboardDTO {
#XmlElement(name = "Value", namespace = "http://schemas.datacontract.org/2004/07/PS.SharedWebServices.Response")
private double value;
#XmlElement(name = "Code", namespace = "http://schemas.datacontract.org/2004/07/PS.SharedWebServices.Response")
private String code;
#XmlElement(name = "Name", namespace = "http://schemas.datacontract.org/2004/07/PS.SharedWebServices.Response")
private String Nnme;
}
Now using an xml file (for convenience):
<Response xmlns="http://tempuri.org/">
<Result xmlns:a="http://schemas.datacontract.org/2004/07/PS.SharedWebServices.Response" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Errors />
<a:Count>329</a:Count>
<a:Return>SUCCESS</a:Return>
<a:DashboardDTOs>
<a:DashboardDTO>
<a:Value>28.58</a:Value>
<a:Code>O001</a:Code>
<a:Name>Test2</a:Name>
</a:DashboardDTO>
<a:DashboardDTO>
<a:Value>40.22</a:Value>
<a:Code>O002</a:Code>
<a:Name>Test2</a:Name>
</a:DashboardDTO>
<a:DashboardDTO>
<a:Value>54.11</a:Value>
<a:Code>O003</a:Code>
<a:Name>Test3</a:Name>
</a:DashboardDTO>
</a:DashboardDTOs>
</Result>
</Response>
And trying if the marshaling/unmarshaling works in a simple main:
public static void main(String[] args) {
try {
File file = new File("response.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(Response.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Response response = (Response) jaxbUnmarshaller.unmarshal(file);
System.out.println(response);
} catch (JAXBException e) {
e.printStackTrace();
}
}
works fine for me. Try this in the main and if it works, but then it does not in your application, let me know and we can see what else can be wrong there.

Related

how to send post with MULTIPART_FORM_DATA with #RequestBody in restful service

I want to send #RequestBody as Post request to restful service.In #RequestBody I have id, title, aboutMe, and image file.I set produces = MediaType.MULTIPART_FORM_DATA_VALUEbut when i check in rest I get error that says
406 not acceptable .How can I solve it?
My controller:
#RequestMapping(value = "aboutMe", method = RequestMethod.POST, produces = MediaType.MULTIPART_FORM_DATA_VALUE)
public String saveAboutMe(#RequestBody Author author) {
authorService.saveAboutMe(author);
return "saved";
}
And entity
#Entity
#Table(name = "author")
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "HIBERNATE_SEQUENCE")
#SequenceGenerator(name = "HIBERNATE_SEQUENCE", sequenceName = "HIBERNATE_SEQUENCE", allocationSize = 1, initialValue = 1)
private int id;
#Column(name = "title")
private String title;
#JsonFormat(pattern = "yyyy-MM-dd HH:mm")
#Column(name = "dateOfAuthor")
#Temporal(TemporalType.TIMESTAMP)
private Date modifiedTime;
#Column(name = "aboutMe", length = 10000)
private String aboutMe;
#Column(name = "image")
#Lob
private Blob image;
}
Screenshot I get from rest error
You can not pass with #RequestBody and Blob object, image should be MultipartFile object. So for working code:
1) Inject SessionFactory into controller class or in any class where you want to convert Mulitpart to Blob object
#Autowired
private SessionFactory sessionFactory;
2) Changes for Controller class:
#RequestMapping(value = "aboutMe", method = RequestMethod.POST)
public String saveAboutMe(Author author,#RequestPart("image") MultipartFile imageFile) {
//Convert Multipart file into BLOB
try {
Blob image = Hibernate.getLobCreator(sessionFactory.getCurrentSession()).createBlob(authorImage.getInputStream(),authorImage.getSize());
author.setImage(image);
} catch (HibernateException | IOException exception) {
log.error(exception.getMessage(),exception);
}
authorService.saveAboutMe(author);
return "saved";
}

Mapstruct: how to map multiple fields from DTO to an object in Entity?

i have this DTO:
#NoArgsConstructor
public class DataDTO implements DTO {
private static final long serialVersionUID = -5105904799152965475L;
private Long deviceId;
private OffsetDateTime generatedOn;
public Long getDeviceId() {
return deviceId;
}
public void setDeviceId(Long deviceId) {
this.deviceId = deviceId;
}
public OffsetDateTime getGeneratedOn() {
return generatedOn;
}
public void setGeneratedOn(OffsetDateTime generatedOn) {
this.generatedOn = generatedOn;
}
}
i have this MongoDB document:
#Document(collection = "data")
#EqualsAndHashCode
public class DataDocument {
private static final long serialVersionUID = 1772572723546311500L;
#Id
private IdByDeviceIdAndGeneratedOn id;
public DataDocument() {
}
public IdByDeviceIdAndGeneratedOn getId() {
return id;
}
public void setId(IdByDeviceIdAndGeneratedOn id) {
this.id = id;
}
}
and this is the #Id class for MongoDB Document:
#EqualsAndHashCode
#ToString
public class IdByDeviceIdAndGeneratedOn {
#Id
private final Long deviceId;
#Id
#Field("generated_on")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private final OffsetDateTime generatedOn;
public IdByDeviceIdAndGeneratedOn(final Long deviceId, final OffsetDateTime generatedOn) {
this.deviceId = Objects.requireNonNull(deviceId);
this.generatedOn = Objects.requireNonNull(generatedOn);
}
public Long getDeviceId() {
return deviceId;
}
public OffsetDateTime getGeneratedOn() {
return generatedOn;
}
}
this is the mapper for this Key class:
#Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR, componentModel = "spring")
public interface IdByDeviceIdAndGeneratedOnMapper {
default IdByDeviceIdAndGeneratedOn toId(final Long deviceId, final OffsetDateTime generatedOn) {
return new IdByDeviceIdAndGeneratedOn(deviceId, generatedOn);
}
default Long getDeviceId(final IdByDeviceIdAndGeneratedOn id) {
return id.getDeviceId();
}
default OffsetDateTime getGeneratedOn(final IdByDeviceIdAndGeneratedOn id) {
return id.getGeneratedOn();
}
and this is the #Mapper for DataDTO and DataDocument:
#Mapper( unmappedTargetPolicy = ReportingPolicy.ERROR,
uses = {IdByDeviceIdAndGeneratedOnMapper.class,
AccelerometerDocumentMapper.class,
GpsDocumentMapper.class,
GsmDocumentMapper.class
})
public interface DataDocumentMapper extends DocumentMapper<DataDTO, DataDocument> {
}
this is the generic mapper:
/**
* Contract for a generic dto to entity mapper.
*
* #param <DTO> - DTO source type parameter.
* #param <DOCUMENT> - MongoDB Document destination type parameter.
*/
public interface DocumentMapper<DTO, DOCUMENT> {
DOCUMENT toDocument(DTO dto);
DTO toDto(DOCUMENT document);
}
Currently i'm receiving this errors:
for MongoDB Data docment:
Unmapped target property: "id".
for DTO:
Unmapped target properties: "deviceId, generatedOn".
How to solve this errors without loosing immutability of Id class?
What you are trying to do is to use (using constructors to construct objects) is not yet supported. There is an open issue for it #73.
However, you can achieve what you are looking for by using Object factories, this is for the toDocument mapping, for the toDto mapping you can use nested source mappings.
Your mapper would look like:
#Mapper(uses = {AccelerometerDocumentMapper.class,
GpsDocumentMapper.class,
GsmDocumentMapper.class},
componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface DataDocumentMapper extends DocumentMapper<DataDTO, DataDocument> {
#Mapping(target = "id", source = "dto")
#Override
DataDocument toDocument(DataDTO dto);
#ObjectFactory
default IdByDeviceIdAndGeneratedOn createId(DataDTO dto) {
return dto == null ? null : new IdByDeviceIdAndGeneratedOn(dto.getDeviceId(), dto.getGeneratedOn());
}
#Mapping(target = "deviceId", source = "id.deviceId")
#Mapping(target = "generatedOn", source = "id.generatedOn")
#Override
DataDTO toDto(DataDocument document);
}
NB: You can also make DataDocumentMapper abstract class and make the createId method protected, in case you don't want to expose it in the interface
this is solved my problem, but this doesnt look elegant.
Maybe there is more elegant way?
#Mapper(uses = {AccelerometerDocumentMapper.class,
GpsDocumentMapper.class,
GsmDocumentMapper.class},
imports = {IdByDeviceIdAndGeneratedOn.class},
componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface DataDocumentMapper extends DocumentMapper<DataDTO, DataDocument> {
#Override
#Mapping(target = "id", expression = "java( new IdByDeviceIdAndGeneratedOn(dto.getDeviceId(), dto.getGeneratedOn()) )")
DataDocument toDocument(DataDTO dto);
#Override
#Mapping(target = "deviceId", expression = "java( document.getId().getDeviceId() )")
#Mapping(target = "generatedOn", expression = "java( document.getId().getGeneratedOn() )")
DataDTO toDto(DataDocument document);
}

Why is a field of my entity class null?

I have these two entities in a one to many relation:
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Short id;
#Basic(optional = false)
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "categoryId")
private Collection<Product> productCollection;
...
#XmlTransient
public Collection<Product> getProductCollection() {
return productCollection;
}
...
and
public class Product implements Serializable {
...
#JoinColumn(name = "category_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Category categoryId;
...
generated with NetBeans. The problem is that when the method getProductCollection() is called by the ControllerServlet the Collection of Product is null.
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String userPath = request.getServletPath();
Category selectedCategory;
Collection<Product> categoryProducts;
// if category page is requested
if (userPath.equals("/category")) {
// get categoryId from request
String categoryId = request.getQueryString();
if (categoryId != null) {
// get selected category
selectedCategory = categoryFacade.find(Short.parseShort(categoryId));
// place selected category in request scope
request.setAttribute("selectedCategory", selectedCategory);
// get all products for selected category
categoryProducts = selectedCategory.getProductCollection();
// place category products in request scope
request.setAttribute("categoryProducts", categoryProducts);
}
Notice the null value of productCollection when other fields has been yet initialized
Edit 1: I declared the categoryFacade in the ControllerServlet applying the #EJB annotation
public class ControllerServlet extends HttpServlet {
#EJB
private CategoryFacade categoryFacade;
Edit 2: Here is the persistence.xml document
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="AffableBeanPU" transaction-type="JTA">
<jta-data-source>jdbc/affablebean</jta-data-source>
<properties/>
</persistence-unit>
</persistence>
Edit 3: I'm using TomEE 7.0.2
Try to initialize the Collection empty like:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "categoryId")
private Collection<Product> productCollection = new HashSet<>();
Then you won't have null values even when there are no results in the lazy loaded relationship. If there are loaded values, they will be added to the collection.

Super class elements not returned with class in morphia and mongoDB with jersey

I have base class as
#XmlRootElement
public abstract class BaseDO {
#Id
protected ObjectId id;
/**
* We'll only provide getters for these attributes, setting is done in #PrePersist.
*/
protected Date creationDate;
protected Date lastChange;
.....and user class as:
#Entity(value = "user", noClassnameStored = true)
#XmlRootElement(name = "user")
#XmlSeeAlso({BaseDO.class})
public class AtsUser extends BaseDO {
public static enum UserStatus {
CREATED, ACTIVE, INACTIVE, DELETED, CLOSED
}
#Indexed(unique = false)
private String firstName;
#Indexed(unique = false)
private String lastName;
#Indexed(unique = false)
private String email;
private String password;
#Embedded
private List<UserRoleDO> roles = new ArrayList<UserRoleDO>();
// private String userId; //TODO add this later
private UserStatus status;
private String success;
.....
the REST API is as follows:
#Path("user/validate")
public class AtsUserValidationService {
private AtsUserDao dao;
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
#GET #Path("/query")
#Produces(MediaType.APPLICATION_JSON)
public AtsUser getUserByEmailn(#QueryParam("email") String email) {
System.out.println("in getUserByEmailn");
dao = new AtsUserMongodbDao();
System.out.println("firstName " + email);
AtsUser atsUser = dao.getAtsUsersByEmail(email) ;
return atsUser ;
}
....
The morphia Dao is as follows:
#Override
public AtsUser getAtsUsersByEmail(String email) {
AtsUser atsUser = null;
if ((email == null) || email.isEmpty() ) {
return null;
}
System.out.println("getAtsUsersByEmail:" + email);
Query<AtsUser> query = mongoDatastore.find(AtsUser.class);
query.field("email").equal(email);
atsUser = query.get();
return atsUser;
}
.....
When I debug, I see the id field and creationDate fields in the java code, but the JSON does not contain that. Here is what my JSON looks like.
{
"id": null,
"code": "admin",
"desc": "admin",
"email": "admin#aa.com",
"firstName": "admin",
"lastName": "admin",
"password": "admin",
"status": "CREATED"
}
Why my id is null and how can i get elements from base class to show up in the JSON ?
I believe you need to annotate the base class with #Entity as well. It worked for me (using morphia 0.109).

Spring-MVC, InheritanceType.JOINED, and automatic mapping of JSON objects to entities with Jackson

We're trying to write an API that creates elements of different types. The elements have a JPA entity representation. The following code shows how our basic element structure looks like (simplified):
import javax.persistence.*;
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Element {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column
private String type;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
Every element implementation looks different but this example should be enough:
import javax.persistence.Column;
import javax.persistence.Entity;
#Entity
public class SpecializedElement1 extends Element {
#Column
private String attribute;
public String getAttribute() {
return attribute;
}
public void setAttribute(String attribute) {
this.attribute = attribute;
}
}
We use Jackson and a typical controller action looks like this:
#RequestMapping(value = "/createElement", method = RequestMethod.POST)
#ResponseBody
public HashMap<String, Object> create(#RequestBody Element element) {
HashMap<String, Object> response = new HashMap<String, Object>()
response.put("element", element);
response.put("status", "success");
return response;
}
A typical request body looks like this:
{
"type": "constantStringForSpecializedElement1"
"text": "Bacon ipsum dolor sit amet cow bacon drumstick shankle ham hock hamburger."
}
As you will see: This does not work since Jackson does not know how to map this object to SpecializedElement1.
The question is: How can I make this work?
I figured it out. That's the solution:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#JsonTypeInfo(
// We use the name defined in #JsonSubTypes.Type to map a type to its implementation.
use = JsonTypeInfo.Id.NAME,
// The information that stores the mapping information is a property.
include = JsonTypeInfo.As.PROPERTY,
// The property is called "type".
property = "type"
)
#JsonSubTypes({
#JsonSubTypes.Type(value = SpecializedElement1.class, name = "specializedElement1"),
#JsonSubTypes.Type(value = SpecializedElement1.class, name = "specializedElement2")
})
public class Element {
// ....
}
This controller action works as expected...
#RequestMapping(value = "/create", method = RequestMethod.POST)
#ResponseBody
public Map<String, Object> create(#RequestBody Element element) {
if (element == null) {
// Return an error response.
}
try {
return elementService.update(element);
} catch (Exception e) {
// Return an error response.
}
}
... with this request:
POST /create/
... more headers ...
Content-Type: application/json
{
"type": "specializedElement1"
}