Spring MVC – View (JSON, XML, PDF or Excel)

Content negotiation in Spring MVC allows you to view data in different format based on the request media type. Here we will see how to view the same data in different media types “json, xml, pdf & excel”.

spring-mvc-view-json-xml-pdf-excel

gae-demo

Objectives:

  • How to configure spring to respond to different media type request?
  • How to return data in json, xml, pdf or excel format?
  • How to write PDF view using iText & Excel view using Apache POI for Excel 2007+?

Environment & Tools:

  • Eclipse
  • Maven
  • Jetty (or any other server)

Libraries:

About This Sample:

  • Client will send GET request to view data “list of articles” in one of the available formats “mime type” (json, xml, pdf or excel).
  • The request is mapped to Spring MVC controller which will respond to the client with the data in requested format.
  • Spring is configured to detect the requested content type and prepare data in required format accordingly.
  • Formatting data in JSON and XML is straight forward no coding is needed if the default output is meeting our requirement.
  • However formatting the data in PDF or Excel needs some coding to prepare the layout of these documents. For PDF we will be using iText and for Excel we will be using Apache POI
  • The data source in this example “list of articles” is an XML file “articles.xml” which will be converted into Java object “LinkedList<Article>” using Castor XML mapping before responding to the client. The data source can be DB, text file, or just hard coded data.

( 1 ) Project Structure

spring-mvc-view-json-xml-pdf-excel_project

( 2 ) Data Model & Source

  • src/main/java/com/hmkcode/vo/Article.java

This is a class is data model that will hold our data

package com.hmkcode.vo;

import java.util.LinkedList;
import java.util.List;

public class Article {

	private String title;
	private String url;
	private List<String> categories;
	private List<String> tags;

	//getters & setters...

}
  • articles.xml

This XML file is just the source of data to be sent to the client. The data source can be also database, text file or hard-coded data.

<?xml version="1.0" encoding="UTF-8"?>
<linked-list>
	<article xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:type="java:com.hmkcode.vo.Article">
		<categories xsi:type="java:java.lang.String">Spring</categories>
		<tags xsi:type="java:java.lang.String">Spring</tags>
		<tags xsi:type="java:java.lang.String">JSON</tags>
		<tags xsi:type="java:java.lang.String">XML</tags>
		<tags xsi:type="java:java.lang.String">iText</tags>
		<tags xsi:type="java:java.lang.String">Apache POI</tags>
		<url>http://hmkcode.com/spring-mvc-view-json-xml-pdf-or-excel/</url>
		<title> Spring MVC View (JSON, XML, PDF or Excel) </title>
	</article>
.....
.....
</linked-list>

( 3 ) Web & Spring Configuration

  • Controller.java
  • rest-servlet.xml
  • web.xml
  • index.html
  • /src/main/java/com/hmkcode/controllers/Controller.java

This class is the controller that will receive client request. Client can call this controller by send GET request to /rest/controller/get URL.

package com.hmkcode.controllers;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.LinkedList;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.Unmarshaller;
import org.exolab.castor.xml.ValidationException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.hmkcode.vo.Article;

@Controller
@RequestMapping("/controller")
public class MyController {

	public MyController(){
		System.out.println("Init MyController");
	}

	@RequestMapping(value = "/get", method = RequestMethod.GET)
	 public  LinkedList<Article>  get(Model model) {                 

		FileReader reader;

		LinkedList<Article> articles = null;

		try {

			reader = new FileReader("articles.xml");
            //convert "unmarshal" data from XML "articles.xml" to Java object LinkedList<Article>
			articles = (LinkedList) Unmarshaller.unmarshal(LinkedList.class, reader);
			model.addAttribute("articles",articles);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (MarshalException e) {
			e.printStackTrace();
		} catch (ValidationException e) {
			e.printStackTrace();
		}

		return articles;

	}

}
  • /src/main/webapp/WEB-INF/rest-servlet.xml

In this file we configure spring to handle the request of different media type by setting up ContentNegotiatingViewResolver bean. Two properties of ContentNegotiatingViewResolver need to be set  “mediaTypes” & “defaultViews“. The first property “mediaTypes” will contain a map of accepted types, the key of this map is the extension that will be passed with request e.g. /rest/controller/get.xlsx while the value is the media type standard name e.g. application/json. The second “defaultViews” list the beans that will generate the data in the requested format.

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="

http://www.springframework.org/schema/beans


http://www.springframework.org/schema/beans/spring-beans-3.0.xsd


http://www.springframework.org/schema/context


http://www.springframework.org/schema/context/spring-context-3.0.xsd


http://www.springframework.org/schema/mvc


http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

	<context:component-scan base-package="com.hmkcode.controllers" />
	<mvc:annotation-driven />

	<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">      
		<property name="order" value="1" />      
		<property name="mediaTypes">        
			<map>           
			  <entry key="json" value="application/json" />           
			  <entry key="xml" value="application/xml" />           
			  <entry key="pdf" value="application/pdf" /> 
			  <entry key="xlsx" value="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />        
			</map>      
		</property>       

		<property name="defaultViews">        
			<list>          
				<!-- JSON View -->          
				<bean            class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">          
				</bean>          

				<!--  XML view -->            
				<bean class="org.springframework.web.servlet.view.xml.MarshallingView">            
				<constructor-arg>                
					<bean class="org.springframework.oxm.castor.CastorMarshaller">                   	          
					</bean>            
				</constructor-arg>          
				</bean>          

				<!--  PDF view -->          
				<bean class="com.hmkcode.view.PDFView">             
				</bean>

				<!--  XLSX "Excel" view -->          
				<bean class="com.hmkcode.view.ExcelView">             
				</bean>
			</list>      
		</property>      

		<property name="ignoreAcceptHeader" value="true" />     
	</bean>
</beans>
  • /src/main/webapp/WEB-INF/web.xml

This is the standard web.xml to define our servlets. Here we define the URL pattern of the requests to be passed to Spring DispatcherServlet.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>SpringMVC</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  <servlet>
  	<servlet-name>rest</servlet-name>  	           
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  </servlet>
  <servlet-mapping>
  	<servlet-name>rest</servlet-name>
  	<url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>
  • /src/main/webapp/index.html

Just a simple file to display the first page of our web app. Twitter bootstrap is used just for good looking design!.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Spring MVC - View (JSON, XML, PDF, Excel)</title>
  	<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
</head>

<body>

<h1>Spring MVC - View (JSON, XML, PDF, Excel)</h1>
<div style="margin:10px;width:700px;text-align:center">    
	<a href="/spring-mvc-json-pdf-xls/rest/controller/get.json" class="label label-info">JSON</a>
	<a href="/spring-mvc-json-pdf-xls/rest/controller/get.xml" class="label label-info">XML</a>
	<a href="/spring-mvc-json-pdf-xls/rest/controller/get.pdf" class="label label-info">PDF</a>
	<a href="/spring-mvc-json-pdf-xls/rest/controller/get.xlsx" class="label label-info">Excel</a>
</div>

</body>
</html>

( 4 ) Views PDF & Excel

Spring provides an abstract class org.springframework.web.servlet.view.document.AbstractPdfView that we can extends to write our PDF view implementation. This class supports only old version of iText “Bruno Lowagie’s iText“. If we would like to use the newer version of iText then we need to rewrite AbstractPdfView to be compatible with iText (5.x).

Spring provides abstract class org.springframework.web.servlet.view.document.AbstractExcelView that we can extends to write our Excel view implementation. This class support only Excel 97 – 2007 or “.xls” file. If we want to create Excel 2007+  or “.xlsx” document we need to rewrite AbstractExcelView to be compatible with POI-XSSF.

  • /src/main/java/com/hmkcode/view/PDFView.java

This is class create our PDF document using iText (5.4.2)

package com.hmkcode.view;

import java.util.LinkedList;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.hmkcode.view.abstractview.AbstractPdfView;
import com.hmkcode.vo.Article;
import com.itextpdf.text.Anchor;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.Font;
import com.itextpdf.text.Font.FontFamily;
import com.itextpdf.text.pdf.PdfWriter;

public class PDFView extends AbstractPdfView {

	protected void buildPdfDocument(        
			Map<String, Object> model,        
			Document document,        
			PdfWriter writer,        
			HttpServletRequest req,        
			HttpServletResponse resp)        
					throws Exception {

		// Get data "articles" from model
		@SuppressWarnings("unchecked")
		LinkedList<Article> articles = (LinkedList<Article>) model.get("articles");

		// Fonts
		Font fontTitle = new Font(FontFamily.TIMES_ROMAN, 14, Font.BOLD, BaseColor.BLACK);
		Font fontTag = new Font(FontFamily.HELVETICA, 10, Font.BOLD, BaseColor.WHITE);

		for(Article article:articles){

			// 1.Title
			document.add(new Chunk("Title: "));
			Chunk title = new Chunk(article.getTitle(), fontTitle);
			document.add(title);
			document.add(new Chunk(" "));

			// -- newline
			document.add(Chunk.NEWLINE);

			// 2.URL
			document.add(new Chunk("URL: "));
			Anchor url  = new Anchor(article.getUrl());
			url.setName(article.getUrl());
			url.setReference(article.getUrl());
			document.add(url);

			// -- newline
			document.add(Chunk.NEWLINE);

			// 3.Categories
			Chunk cat = null;
			document.add(new Chunk("Category: "));
			for(String category:article.getCategories()){
				cat =  new Chunk(category, fontTag);
				cat.setBackground(new BaseColor(72, 121, 145), 1f, 0.5f, 1f, 1.5f);
    			document.add(cat);
    			document.add(new Chunk(" "));

      		}

			// -- newline
			document.add(Chunk.NEWLINE);

			// 4.Tags
			Chunk tg = null;
			document.add(new Chunk("Tags: "));
			for(String tag:article.getTags()){
				tg =  new Chunk(tag, fontTag);
				tg.setBackground(new BaseColor(97, 97, 97), 1f, 0.5f, 1f, 1.5f);
    			document.add(tg);
    			document.add(new Chunk(" "));

      		}

			// -- newline
			document.add(Chunk.NEWLINE);
			document.add(Chunk.NEWLINE);

		}

	}

}
  • /src/main/java/com/hmkcode/view/ExcelView.java

This class create Excel document using Apache POI-XSSF

package com.hmkcode.view;

import java.util.LinkedList;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.poi.ss.formula.functions.Hyperlink;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import com.hmkcode.view.abstractview.AbstractExcelView;
import com.hmkcode.vo.Article;

public class ExcelView extends AbstractExcelView {

	@Override
	protected void buildExcelDocument(Map<String, Object> model,
			Workbook workbook, HttpServletRequest request,
			HttpServletResponse response) throws Exception {

		Sheet sheet = workbook.createSheet("sheet 1");

		@SuppressWarnings("unchecked")
		LinkedList<Article> articles = (LinkedList<Article>) model.get("articles");

		Row row = null;
		Cell cell = null;
		int r = 0;
		int c = 0;

		//Style for header cell
		CellStyle style = workbook.createCellStyle();
		style.setFillForegroundColor(IndexedColors.GREY_40_PERCENT.index);
		style.setFillPattern(CellStyle.SOLID_FOREGROUND);
		style.setAlignment(CellStyle.ALIGN_CENTER);

		//Create header cells
		row = sheet.createRow(r++);

		cell = row.createCell(c++);
		cell.setCellStyle(style);
		cell.setCellValue("Title");

		cell = row.createCell(c++);
		cell.setCellStyle(style);
		cell.setCellValue("URL");

		cell = row.createCell(c++);
		cell.setCellStyle(style);
		cell.setCellValue("Categories");

		cell = row.createCell(c++);
		cell.setCellStyle(style);
		cell.setCellValue("Tags");

		//Create data cell
		for(Article article:articles){
			row = sheet.createRow(r++);
			c = 0;
			row.createCell(c++).setCellValue(article.getTitle());
			row.createCell(c++).setCellValue(article.getUrl());
			row.createCell(c++).setCellValue(article.getCategories().toString());
			row.createCell(c++).setCellValue(article.getTags().toString());

		}
		for(int i = 0 ; i < 4; i++)
			sheet.autoSizeColumn(i, true);

	}

}

( 5 ) Run

This sample code is ready to run using Maven command

>mvn jetty:run

Alternatively, you can create the war file and deploy it into a server of your choice.

spring-mvc-view-json-xml-pdf-excel_run

JSON

XML

PDF “Sample PDF

Excel

Source Code @ GitHub

2 thoughts on “Spring MVC – View (JSON, XML, PDF or Excel)

  1. Samraj

    Good Post. I used it.
    When i send data as JSON object i get “415 Unsupported Media Type”. Can you tell me how to fix it?

    Thanks
    Samraj

Comments are closed.