hibernate entity to json - java

i use Hibernate 4 and Spring 3.
i have two entity.
Book entity
#Entity
#Table(name = "book")
public class Book implements Serializable {
public Book() {
}
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne()
#JoinColumn( name = "author_id" )
private Author author;
private String name;
private int pages;
#Version
#Column( name = "VERSION")
private int version;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPages() {
return pages;
}
public void setPages(int pages) {
this.pages = pages;
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
}
and Author entity
#Entity
#Table(name = "author")
public class Author implements Serializable {
public Author() {
}
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY)
private int id;
private String name;
#OneToMany( mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Book> books = new HashSet<Book>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Book> getBooks() {
return books;
}
public void setBooks(Set<Book> books) {
this.books = books;
}
public void addBook(Book book) {
book.setAuthor(this);
getBooks().add(book);
}
public void removeBook(Book book) {
getBooks().remove(book);
}
}
and JSON depend in pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>2.1.2</version>
</dependency>
My Root-context is here -
<!-- Root Context: defines shared resources visible to all other web components -->
<context:annotation-config/>
<context:component-scan base-package="org.jar.libs.dao" />
<context:component-scan base-package="org.jar.libs.service" />
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="jspViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3306/hibernate"
p:username="root" p:password="root" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>org.jar.libs.domain.Book</value>
<value>org.jar.libs.domain.Author</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
...servlet-context.xml
<!-- Enables the Spring MVC #Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by #Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="org.jar.libs.controller" />
Controller.
#Controller
#RequestMapping (value = "books/rest")
public class BookController {
#Autowired
private BookService bookService;
// logger
private static final Logger logger = LoggerFactory.getLogger(BookController.class);
#SuppressWarnings("unchecked")
#RequestMapping( method = RequestMethod.GET )
public #ResponseBody List<Book> getBook() {
List<Book> res = bookService.findAll();
return res;
}
}
findAll in my DAO :
public List<Book> findAll() {
Session session = sessionFactory.getCurrentSession();
List<Book> result = (List<Book>) session.createQuery("select c from Book c").list();
return result;
}
in debug i see that method return 2 records, but Spring can not convert result to JSON and return 406 HTTP error. What's wrong?
I attach image what i see in debug. - http://tinypic.com/view.php?pic=35kvi9i&s=6

Generally, when you call getter methods of entity classes(which returns relation object) out of transaction, then you get LazyInitializationExceptions.
That's what might be happening in your case if you are converting entity class objects(retrieved from query) to json out of transaction.
I had same issue, I converted my entity object retrieved by hibernate to json in controller. As controller was out of transaction(Transaction at service layer), while converting to json, getter methods of entity class objects are called and I got LazyInitializationException. Which obstructed object conversion to json, and response was not returned.
My solution, Try this :
#SuppressWarnings("unchecked")
#RequestMapping( method = RequestMethod.GET )
public #ResponseBody List<Book> getBook() {
List<Book> res = bookService.findAll();
for(Book book : res) {
book.getAuthor().setBooks(null);
}
return res;
}

As others have suggested,
I would really not advise you to try to JSON serialize (or actually perform any serialization) of hibernate entities.
You must remember that the fetched entities are actually "proxified" objects (Hibernate uses ASM, CGLIB and other "dynamic proxiy" frameworks).
As a result for example, collections get replaced with [PersistenceBags] which may be initialized "lazily" , and cause you hibernate exceptions 1.
But the problems do not stop there, you may see issues when trying to serialize an Hibernate custom type
I know this might sound you like writing "boillerplate" code but you might end up coding DTOs - data transfer objects which will take the entity returned from your DAL, and transform them to an object that can be serialized.
You can use a framework like dozer in order to ease development of serialization between an entity to a DTO.

Try using these two Jackson artifacts instead
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.9</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.9</version>
</dependency>
Also on your controller try by changing it to -
#SuppressWarnings("unchecked")
#RequestMapping( method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE )
public #ResponseBody List<Book> getBook() {
Lastly, make sure your view is making a json request.

Related

com.sun.jdi.invocationexception on method getOne(id) Hibernate + Spring MVC + JPA Repository

Im doing a Spring MVC project and im stucked in this problem.
I have an Entity "TipoDoc", his own Service and Repository using JPA Repository, in my service I have a getAll() method than calls the findAll() JPA Repository method and it works fine, but when I want to use a method to get one by id, im receiving a null object whatever id I send to the method.
So, I started to debug searching the problem and I found a com.sun.jdi.invocationexception in response when Hibernate have to execute the method getOne() from JPA Repository.
I dont know what is wrong in my code, or how can I get more details from the exception.. Im using log4j for loging but i dont know how catch that exception in the log..
Im using MySql Database
Here is my code
#Entity
#Table(name = "TiposDocumento")
public class TipoDoc
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idTipoDocumento")
private long id;
private String descripcion;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
}
The Service
#Service
public class TipoDocService {
private final TipoDocRepo tipoDocRepo;
#Autowired
public TipoDocService(TipoDocRepo tipoDocRepo) {
this.tipoDocRepo = tipoDocRepo;
}
public List<TipoDoc> getAll() {
return (List<TipoDoc>)tipoDocRepo.findAll();
}
public TipoDoc getById(Long id) {
return (TipoDoc) tipoDocRepo.getOne(id);
}
}
The Repository
public interface TipoDocRepo extends JpaRepository<TipoDoc, Long>{
}
The Controller
#Controller
public class ClientController
{
private static final Logger logger = Logger.getLogger(ClientController.class);
private final ClienteService clienteService;
private final TipoDocService tipoDocService;
private final EstadoCivilService estadoCivilService;
private final ProvinciaService provinciaService;
private final LocalidadService localidadService;
private final CondIvaService condIvaService;
#Autowired
public ClientController(ClienteService clienteService, TipoDocService tipoDocService, EstadoCivilService estadoCivilService,
ProvinciaService provinciaService, LocalidadService localidadService, CondIvaService condIvaService) {
this.clienteService = clienteService;
this.tipoDocService = tipoDocService;
this.estadoCivilService = estadoCivilService;
this.provinciaService = provinciaService;
this.localidadService = localidadService;
this.condIvaService = condIvaService;
}
#RequestMapping("/Clientes")
public ModelAndView formularioCliente()
{
ModelAndView mav = new ModelAndView("clientes");
mav.getModel().put("cliente",new Cliente());
mav.getModel().put("tiposDoc", tipoDocService.getAll()); //Works fine, tiposDoc={{1,DNI};{2,Passaport};{3,LC}}
TipoDoc tipoDoc = tipoDocService.getById((long) 1); //not working tipoDoc={0,null} when it have to be {1,DNI}
mav.getModel().put("estadosCiviles", estadoCivilService.getAll());
mav.getModel().put("provincias", provinciaService.getAll());
mav.getModel().put("localidades", localidadService.getAll());
mav.getModel().put("condicionesIva", condIvaService.getAll());
return mav;
}
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence 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"
version="2.1">
<persistence-unit name="OFYS">
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="admin" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/OFYS" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>
</properties>
</persistence-unit>
</persistence>
And this is what can I find debuging
EDIT.
Here is the full description from the exception I can get if u dont see the image
com.sun.jdi.invocationexception: Exception ocurred in target VM ocurred invoking method.
The object does not exist in the database with the given id. Apparently you have a proxy of that object in your persistence context which is returned here. When accessing the object it tries to actually load it from the database and fails because there is no row with that id.

Error creating bean with name 'homeController': Injection of autowired dependencies failed

I am trying to create e-commerce in spring. After including "Hibernate" and "H2" database in my project, I get the error. The error is given below. I am trying very much but not found any solution.
Error:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'homeController': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private com.home.dao.ProductDao
com.home.controller.homeController.productDao; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'productDaoImpl': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private org.hibernate.SessionFactory
com.home.dao.impl.ProductDaoImpl.sessionFactory; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'sessionFactory' defined in ServletContext
resource [/WEB-INF/applicationContext.xml]: Invocation of init method
failed; nested exception is
org.hibernate.exception.GenericJDBCException: Unable to open JDBC
Connection for DDL execution
applicationContext.xml
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:~/test" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
<property name="packagesToScan">
<list>
<value>com.home</value>
</list>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
home-servlet.xml
<context:component-scan base-package="com.home">
<context:include-filter type="aspectj" expression="com.home.*" />
</context:component-scan>
<mvc:annotation-driven />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<mvc:resources mapping="/resources/**"
location="/WEB-INF/resources/" cache-period="31556926" />
<tx:annotation-driven />
web.xml
<display-name>Archetype Created Web Application</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/home-servlet.xml,
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<servlet>
<servlet-name>home</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>home</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
homeController.java
#Controller
#Configuration
public class homeController {
#Autowired
private ProductDao productDao;
#RequestMapping("/")
public String home() {
return "views/home";
}
#RequestMapping("/productList")
public String getProducts(Model model) {
List<Product> products = productDao.getAllProducts();
model.addAttribute("products", products);
return "views/productList";
}
#RequestMapping("/productList/viewProduct/{productId}")
public String viewProduct(#PathVariable String productId, Model model) throws IOException{
Product product = productDao.getProductById(productId);
model.addAttribute(product);
return "views/viewProduct";
}
}
ProductDaoImpl.java
#Repository
#Transactional
public class ProductDaoImpl implements ProductDao {
#Autowired
private SessionFactory sessionFactory;
public void addProduct(Product product) {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(product);
session.flush();
}
public Product getProductById(String id) {
Session session = sessionFactory.getCurrentSession();
Product product = (Product) session.get(Product.class, id);
session.flush();
return product;
}
public List<Product> getAllProducts() {
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("from Product");
List<Product> products = query.list();
session.flush();
return products;
}
public void deleteProduct (String id) {
Session session = sessionFactory.getCurrentSession();
session.delete(getProductById(id));
session.flush();
}
}
Product.java Code:
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private String productId;
private String productName;
private String productCategory;
private String productDescription;
private double productPrice;
private String productCondition;
private String productStatus;
private int unitInStock;
private String productManufacturer;
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductCategory() {
return productCategory;
}
public void setProductCategory(String productCategory) {
this.productCategory = productCategory;
}
public String getProductDescription() {
return productDescription;
}
public void setProductDescription(String productDescription) {
this.productDescription = productDescription;
}
public double getProductPrice() {
return productPrice;
}
public void setProductPrice(double productPrice) {
this.productPrice = productPrice;
}
public String getProductCondition() {
return productCondition;
}
public void setProductCondition(String productCondition) {
this.productCondition = productCondition;
}
public String getProductStatus() {
return productStatus;
}
public void setProductStatus(String productStatus) {
this.productStatus = productStatus;
}
public int getUnitInStock() {
return unitInStock;
}
public void setUnitInStock(int unitInStock) {
this.unitInStock = unitInStock;
}
public String getProductManufacturer() {
return productManufacturer;
}
public void setProductManufacturer(String productManufacturer) {
this.productManufacturer = productManufacturer;
}
}
ProductDao.java Code:
public interface ProductDao {
void addProduct(Product product);
Product getProductById(String id);
List<Product> getAllProducts();
void deleteProduct(String id);
}
Project Structure or Directory Image:
Project Structure or Directory at my Eclipse Oxygen IDE
Finally, I have found my own problems when I use IntelliJ IDEA IDE. Problems are given bellow:
My problem have occurred at pom.xml file. Here, I have used hibernate-core latest version (5.4.0.Final) dependency which is not support import org.hibernate.Query; package and also not support Query query = session.createQuery("from Product"); and Product product = (Product) session.get(Product.class, id); codes at ProductDaoImpl.java class.
I have also used latest version of spring-webmvc, spring-core and spring-orm dependency at pom.xml file. For that it occurs version conflict.
Solution:
Forget Eclipse and avoid it. Please use IntelliJ IDEA. This is very user friendly IDE for Java Spring MVC framework and also show what wrong you do.
Create new project and used hibernate-core 4.0.1.Final version dependency at pom.xml file and also used 4.2.8.RELEASE version of own, spring-core and spring-orm dependencies.
Remove import org.hibernate.Query.query; package from ProductDaoImpl.java class and put import org.hibernate.Query package.
Thank you :)
ProductDao is not a bean..That's why. Repository, controller, service are all of type of bean. Make sure this is which type of bean.....Thank you.

java.lang.IllegalStateException: no matching editors or conversion strategy found

So i'm building a spring 3.2.3.RELEASE / hibernate 4.0.1.FINAL application and i got the following exception
[2017-03-22 09:29:47,860] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory [localhost-startStop-1] Ignoring bean creation exception on FactoryBean type check: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginService' defined in URL [jar:file:/D:/Programmes/apache-tomcat-7.0.33/webapps/perWeb/WEB-INF/lib/perService-2.0.jar!/applicationContext-transactional-service.xml]: Cannot resolve reference to bean 'loginServiceImpl' while setting bean property 'target'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginServiceImpl' defined in URL [jar:file:/D:/Programmes/apache-tomcat-7.0.33/webapps/perWeb/WEB-INF/lib/perService-2.0.jar!/applicationContext-simple-service.xml]: Initialization of bean failed; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'ma.dao.impl.GenericDAO' to required type 'ma.dao.IGenericDAO' for property 'dao'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [ma.dao.impl.GenericDAO] to required type [ma.dao.IGenericDAO] for property 'dao': no matching editors or conversion strategy found
Here is my beans: loginservice
<bean id="loginService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManagerPER" />
</property>
<property name="target">
<ref bean="loginServiceImpl" />
</property>
<property name="transactionAttributes">
<props>
<prop key="loadUserByUsername">
PROPAGATION_REQUIRED,-Exception
</prop>
</props>
</property>
</bean>
loginServiceImpl
<bean id="loginServiceImpl"
class="ma.service.login.LoginService">
<property name="dao">
<ref bean="userDAO" />
</property>
</bean>
UserDAO
<bean id="userDAO"
class="ma.dao.impl.GenericDAO">
<constructor-arg>
<value>
ma.dao.mappings.Utilisateur
</value>
</constructor-arg>
<property name="sessionFactory">
<ref bean="sessionFactoryPER" />
</property>
</bean>
Utilisateur.Java
#Entity
#NamedQueries(
{
#NamedQuery(name="findUtilisateurByName",
query = "select user from Utilisateur user where user.login=:userName"
)
}
)
public class Utilisateur implements java.io.Serializable {
private static final long serialVersionUID = 7214071893495381842L;
private Integer id;
private Profil profil;
private String nom;
private String prenom;
private String login;
private String passwd;
public Utilisateur() {
}
public Utilisateur(Integer id) {
this.id = id;
}
#Id #GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#ManyToOne
public Profil getProfil() {
return profil;
}
public void setProfil(Profil profil) {
this.profil = profil;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getPrenom() {
return prenom;
}
public void setPrenom(String prenom) {
this.prenom = prenom;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}
Am i missing somthing ?
If you need more informations please let me know
It seems like your GenericDao not able to convert to IGenericDao and for that, there might be several reasons like is your GenericDao implements the IGenericDao, etc.
Also the following link might be useful if you are implementing GenericDao pattern:
https://www.codeproject.com/Articles/251166/The-Generic-DAO-pattern-in-Java-with-Spring-and

Accepting / returning XML/JSON request and response - Spring MVC

I need to write a rest service which accepts XML/JSON as a input (POST method) and XML/JSON as a output (based on the input format). I have tried a below approach to achieve this but doesn't helped out.Endpoint method accepts both XML/JSON but while responding it always gives either JSON or XML based on the order specified in #RequestMapping -produces.Any help will be really appreciated.
My endpoint method:
#RequestMapping(value = "/getxmljson", method = RequestMethod.POST,produces={"application/json","application/xml"},
consumes={"application/json", "application/xml"})
public #ResponseBody Student processXMLJsonRequest(#RequestBody Student student)
throws Exception {
System.out.println("*************Inside Controller");
return student;
}
POJO Class: Student.java
import java.io.Serializable;
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#XmlRootElement(name = "student")
#XmlType(propOrder = {"id", "name", "graduationTime", "courses"})
#JsonPropertyOrder({"id", "name", "graduationTime", "courses"})
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String name;
private String graduationTime;
private ArrayList<Course> courses = new ArrayList<Course>();
#XmlElement
public int getId() { return id; }
#XmlElement
public String getName() { return name; }
#XmlElement
public String getGraduationTime() { return graduationTime; }
#XmlElement
public ArrayList<Course> getCourses() { return courses; }
public void setId(int value) { this.id = value; }
public void setName(String value) { this.name = value; }
public void setGraduationTime(String value) { this.graduationTime = value; }
public void setCourses(ArrayList<Course> value) { this.courses = value; }
#JsonIgnore
public String toString() {
return this.name + " - "
+ graduationTime == null? "Unknown" : graduationTime.toString();
}
public Student() {}
public Student(int id, String name, String graduationTime) {
this.id = id;
this.name = name;
this.graduationTime = graduationTime;
}
}
POJO Class: Course.java
import java.io.Serializable;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#XmlRootElement(name = "course")
#XmlType(propOrder = {"courseName", "score"})
#JsonPropertyOrder({"courseName", "score"})
public class Course implements Serializable {
private static final long serialVersionUID = 1L;
private String courseName;
private Integer score;
public #XmlElement String getCourseName() { return courseName; }
public #XmlElement Integer getScore() { return score; }
public void setCourseName(String value) { courseName = value; }
public void setScore(Integer value) { score = value; }
public Course() {}
public Course(String courseName, Integer score) {
this.courseName = courseName;
this.score = score;
}
}
spring-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:sws="http://www.springframework.org/schema/web-services"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:oxm="http://www.springframework.org/schema/oxm"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/web-services
http://www.springframework.org/schema/web-services/web-services-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-4.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.5.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing
infrastructure -->
<!-- Enables the Spring MVC #Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving
up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Configure to plugin JSON as request and response in method handler -->
<beans:bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<beans:property name="messageConverters">
<beans:list>
<beans:ref bean="jsonMessageConverter" />
<beans:ref bean="xmlMessageConverter" />
</beans:list>
</beans:property>
</beans:bean>
<!-- Configure bean to convert JSON to POJO and vice versa -->
<beans:bean id="jsonMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</beans:bean>
<beans:bean id="xmlMessageConverter"
class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter">
</beans:bean>
<beans:bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
</beans:bean>
<beans:bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper" />
<context:component-scan base-package="com.test" />
</beans:beans>
Json Input:
{
"id":2014,
"name":"test",
"graduationtime":"09/05/2014",
"courses":[
{
"courseName":"Math",
"score":150
},
{
"courseName":"Che",
"score":150
}
]
}
XML Input:
<?xml version="1.0" encoding="UTF-8" ?>
<student>
<id>2014</id>
<name>test</name>
<graduationTime>09/05/2014</graduationTime>
<courses>
<courseName>Math</courseName>
<score>150</score>
</courses>
<courses>
<courseName>Che</courseName>
<score>150</score>
</courses>
</student>
The best practice for handling different data formats with the same controller is to let the framework do all the work of figuring out the marshalling and unmarshalling mechanisms.
Step 1: Use minimal controller configuration
#RequestMapping(value = "/getxmljson", method = RequestMethod.POST)
#ResponseBody
public Student processXMLJsonRequest(#RequestBody Student student) {
return student;
}
There is no need to specify consumes and produces here. As an example, consider that you may want this same method to handle other formats in the future such as Google Protocol Buffers, EDI, etc. Keeping the controllers free of consumes and produces will let you add data formats through global configuration instead of having to modify the controller code.
Step 2: Use ContentNegotiatingViewResolver instead of RequestMappingHandlerAdapter
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
</list>
</property>
</bean>
Let the view resolver decide how to read incoming data and how to write it back.
Step 3: Use Accepts and Content-Type HTTP headers
Hitting your controller with the correct HTTP header values will force ContentNegotiatingViewResolver to marshal and unmarshal data automatically using the appropriate data representations.
If you want to exchange data in JSON format, set both headers to application/json. If you want XML instead, set both to application/xml.
If you do not want to use HTTP headers (which ideally you should), you can simply add .json or .xml to the URL and ContentNegotiatingViewResolver will do the rest.
You can check out my sample app that I created using your code snippets that works fine for JSON and XML.
Adding to Manish's answer above, if you don't wanna use xml based configuration use this java based configuration instead-
#Bean
public ViewResolver contentNegotiatingViewResolver() {
ContentNegotiatingViewResolver resolver =
new ContentNegotiatingViewResolver();
List<View> views = new ArrayList<>();
views.add(new MappingJackson2XmlView());
views.add(new MappingJackson2JsonView());
resolver.setDefaultViews(views);
return resolver;
}
Register a filter that intercepts each request, warp the HttpServletRequest into an implementation of HttpServletRequestWrapper and returns the Content-Type value for Accept header. For example, you can register a filter named SameInSameOutFilter like following:
#Component
public class SameInSameOutFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
SameInSameOutRequest wrappedRequest = new SameInSameOutRequest((HttpServletRequest) request);
chain.doFilter(wrappedRequest, response);
}
}
It wraps current request in a SameInSameOutRequest:
public class SameInSameOutRequest extends HttpServletRequestWrapper {
public SameInSameOutRequest(HttpServletRequest request) {
super(request);
}
#Override
public String getHeader(String name) {
if (name.equalsIgnoreCase("accept")) {
return getContentType();
}
return super.getHeader(name);
}
}
This wrapper tells spring mvc to select a HttpMessageConverter based on request's Content-Type value. If request body's Content-Type is application/xml, then the response would be an XML. Otherwise, the response would be JSON.
The other solution is to manually set the Accept header along with Content-Type in each request and avoid all these hacks.
i was facing the same problem like yours. Below is my solution and sample.
Below is maven dependency that you need to include:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.4.3</version>
</dependency>
dispatcher-servlet.xml
<mvc:annotation-driven
content-negotiation-manager="contentManager" />
<bean id="contentManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false" />
<property name="ignoreAcceptHeader" value="false" />
<property name="defaultContentType" value="application/json" />
<property name="useJaf" value="false" />
</bean>
and my #RequestMapping ( you can use your own request mapping )
#RequestMapping(value = "/testXMLJSON",
method = RequestMethod.GET, produces = {
MediaType.APPLICATION_XML_VALUE,
MediaType.APPLICATION_JSON_VALUE })
#ResponseBody
public ArtworkContentMessageType testXMLJSON()
{
//this is GS1 xml standard mapping.
ArtworkContentMessageType resp = new ArtworkContentMessageType();
StandardBusinessDocumentHeader standarBusinessDocumentHeader = new StandardBusinessDocumentHeader();
resp.setStandardBusinessDocumentHeader(standarBusinessDocumentHeader );
ArtworkContentType artWorkContent = new ArtworkContentType();
resp.getArtworkContent().add(artWorkContent);
return resp ;
}
If application/xml is required then, below headers must be present
Content-Type:application/xml
Accept:application/xml
If the resource define as below
#GET
#Path("/{id}")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Student getStudent(#PathParam("id") String id) {
return student(); // logic to retunrs student object
}
Then the request should contains 'accept' header ('application/json' or application/xml'),
then it returns response in json or xml format.
Sample request :
curl -k -X GET -H "accept: application/json" "https://172.17.0.5:8243/service/1.0/222"
Sample Student class
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "student")
public class Student {
private int id;
private String name;
private String collegeName;
private int age;
#XmlAttribute
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#XmlElement
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlElement
public String getCollegeName() {
return collegeName;
}
public void setCollegeName(String collegeName) {
this.collegeName = collegeName;
}
public int getAge() {
return age;
}
#XmlElement
public void setAge(int age) {
this.age = age;
}
}

How can I Convert XML to an Object using Spring 3.0 mvc while making RESTful request

I'm using the Spring 3.0 RC1 framework and I'm currently testing out Spring mvc. I wanted to use Spring mvc to handle restful requests. I have set up my controller to handle the URI request. I am passing in xml with the request. So on the controller I have a method like follows:
public void request(RequestObject request) {
doSomething();
}
I am having a hard time converting the xml to the RequestObject. I haven't seen much documentation on this and I was wondering if anyone could point me in the right direction. I'm guess that you would have to annotate the RequestObject using JAXB or something in order to tell Spring to convert the xml file to RequestObject but I'm not sure.
Thanks for all of your help!!
For converting XML to Java object you can use Apache Digest http://commons.apache.org/digester/. Spring uses it itself internally.
Update
I wasn't aware about this new feature in Spring 3.0. Sorry for misdealing you.
I wrote quick test and this is what you should do.
1) Set up ViewResoler and MessageConverter in -servlet.xml. In my test it looks like this
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean id="person" class="org.springframework.web.servlet.view.xml.MarshallingView">
<property name="contentType" value="application/xml"/>
<property name="marshaller" ref="marshaller"/>
</bean>
<oxm:jaxb2-marshaller id="marshaller">
<oxm:class-to-be-bound name="com.solotionsspring.test.rest.model.Person"/>
</oxm:jaxb2-marshaller>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="marshallingHttpMessageConverter"/>
</list>
</property>
</bean>
<bean id="marshallingHttpMessageConverter"
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<property name="marshaller" ref="marshaller" />
<property name="unmarshaller" ref="marshaller" />
</bean>
2) Add XML structure annotations into your Java class
#XmlRootElement
public class Person {
private String name;
private int age;
private String address;
/**
* #return the name
*/
#XmlElement
public String getName() {
return name;
}
/**
* #param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* #return the age
*/
#XmlElement
public int getAge() {
return age;
}
/**
* #param age the age to set
*/
public void setAge(int age) {
this.age = age;
}
/**
* #return the address
*/
#XmlElement
public String getAddress() {
return address;
}
/**
* #param address the address to set
*/
public void setAddress(String address) {
this.address = address;
}
}
3) Add mapping annotation into your Controller class like
#Controller
public class RestController {
#RequestMapping(value = "/person", method = RequestMethod.PUT)
public ModelMap addPerson(#RequestBody Person newPerson) {
System.out.println("new person: " + newPerson);
return new ModelMap(newPerson);
}
}
Hope this will help you.

Categories

Resources