How to use Jackson to parse a json in http servlet? - json

I send a json from page to server with YUI and how to get data from json in servlet. I use jackson lib 2.4. Thanks!!!
var user = {
userName: username,
password: password,
customerId: customerId
};
new Y.IO().send("http://localhost:7778/MyController", {
method: 'POST',
data: user
});

actually, when you make a request like this
<html>
<body>
<script src="http://yui.yahooapis.com/3.14.1/build/yui/yui-min.js"></script>
<script>
var user = {
userName: 'x1',
password: 'y2',
customerId: 'z3'
};
YUI().use('io-form', function (Y) {
new Y.IO().send("http://localhost:8080/web/MyController", {
method: 'POST',
data: user
});
});
</script>
</body>
</html>
you're not sending a JSON object to the servlet. Instead, you're creating a http request where each property of your javascript object (which is represented in the JSON format) is sent as an http POST property, so you can retrieve these values like this
package mine;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class MyController
*/
#WebServlet("/MyController")
public class MyController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* #see HttpServlet#HttpServlet()
*/
public MyController() {
super();
// TODO Auto-generated constructor stub
}
/**
* #see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map<String, String[]> map = request.getParameterMap();
for(Entry<String,String[]> entry:map.entrySet()){
System.out.println(entry.getKey());
System.out.println(entry.getValue()[0]);
}
}
}
which in turn prints this
userName
x1
password
y2
customerId
z3
so you don't need jackson to parse the data you retrieve from your page.

Related

Spring Boot 2 handling form-data where json are values of keys

I have a situation where I have to implement post method to handle form-data where json are values of keys. Each JSON internally represent an object.
I can get the json as string via RequestParam and then convert to object using Jackson.
#RequestMapping(value = "/rest/patient", consumes = {"multipart/form-data"},
produces = "application/json", method= RequestMethod.POST)
public ResponseEntity<?> savePatient(#RequestParam("patient") String patient ) {
// convert to Patient instance using Jackson
}
Is there any out of box mapping from Spring boot?
I don't believe there is an out of the box mapping.
You could add a GenericConvertor to the WebConversionService that the WebDataBinder uses. You would need to list all your object types. Something like the following:
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
#Component
public class JsonFieldConverter implements GenericConverter {
#Autowired
private ObjectMapper objectMapper;
// Add a new ConvertiblePair for each type you want to convert json
// in a field to using the objectMapper. This example has Patient and Doctor
private static Set<ConvertiblePair> convertibleTypes = new HashSet<>();
static {
convertibleTypes.add(new ConvertiblePair(String.class, Patient.class));
convertibleTypes.add(new ConvertiblePair(String.class, Doctor.class));
}
#Override
public Set<ConvertiblePair> getConvertibleTypes() {
return convertibleTypes;
}
#Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
try {
return objectMapper.readValue(source.toString(), targetType.getType());
} catch (Exception e) {
// TODO deal with the error.
return source;
}
}
}
And an #ControllerAdvice to plug it into the data binder:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.format.WebConversionService;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
#ControllerAdvice
public class JsonFieldConfig {
#Autowired
private JsonFieldConverter jsonFieldConverter;
#InitBinder
private void bindMyCustomValidator(WebDataBinder binder) {
((WebConversionService)binder.getConversionService()).addConverter(jsonFieldConverter);
}
}

A callback method to execute post JSON to object conversion in Jersey JAX RS

I am using Jersey rest services with Jackson API for conversion of JSON String to POJOs. All the member variables of the POJO class need to be validated. I already have a framework in place for validation.
What I want to know is if there is any callback method or mechanism which can call my validation API post the JSON to POJO conversion itself. Doing this would make my job easier as I will not have to call the API at all the places in my Rest service class.
public class MyPojoClass{
private int interestRateCode;
private String name;
//just edited
private List<TestDTO> testObjs;
//Psuedo code
//#PostDeserialization
public String callbackMethod(Object obj){
if(!ValidationAPI.validate(obj))
return "false";
}
}
The TestDTO:
public class TestDTO {
private int var1;
private String stringVar;
public TestDTO() {
System.out.println("This constructor does get called every time");
}
}
Is there any annotation like PostDeserialization to achieve this. This will help me to make every POJO class having only one callback method for validation.
The JSON I am passing is
{"interestRateCode": 101,"name": "T",
"testObjs": [
{"var1" :10, "stringVar": "Arunabh"},
{"var1" :15, "stringVar": "Hejib"}
]}
Anyone who can help me on this problem? Thanks for any help.
One thing you can do is use a Request Filter. In the filter you would:
Get the resource Method using the injected ResourceInfo
Get the entity class by traversing the Method Parameters and checking for the method parameter without any annotations. Unless you're using bean validation where #Valid is used next to the parameter, then the entity parameter always is the parameter with no annotations. This is how we determine the entity parameter. From the parameter, we get the class.
Get the entity objects from the request.
From the entity class, using from reflection, find the Method with the #PostDeserialization annotation.
Call the method using reflection.
Below is a complete test. The ValidationFilter is the class with previous mentioned steps.
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.client.Entity;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import static org.junit.Assert.assertEquals;
/**
* Run like any other JUnit test. A couple required dependencies.
*
* <dependency>
* <groupId>org.glassfish.jersey.test-framework.providers</groupId>
* <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
* <version>${jersey2.version}</version>
* <scope>test</scope>
* </dependency>
* <dependency>
* <groupId>org.glassfish.jersey.media</groupId>
* <artifactId>jersey-media-json-jackson</artifactId>
* <version>${jersey2.version}</version>
* </dependency>
*/
public class PostDeserializationTest extends JerseyTest {
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface PostDeserialization {}
public static class ValidationError extends RuntimeException {
public ValidationError(String message) {
super(message);
}
}
public static class ValidationErrorMapper
implements ExceptionMapper<ValidationError> {
#Override
public Response toResponse(ValidationError ex) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(ex.getMessage())
.build();
}
}
public static class Bean {
public String value;
#PostDeserialization
public void validate() {
if (!"expected".equals(value)) {
throw new ValidationError("value must be 'expected'");
}
}
}
public static class ValidationFilter implements ContainerRequestFilter {
#Context
private ResourceInfo info;
#Override
public void filter(ContainerRequestContext request) throws IOException {
Class<?> entityClass = getEntityClass();
if (entityClass != null) {
final ContainerRequest cr = (ContainerRequest) request;
cr.bufferEntity();
final Object entity = cr.readEntity(entityClass);
findMethodAndValidate(entity);
}
}
private Class<?> getEntityClass() {
final Method rm = info.getResourceMethod();
final Annotation[][] paramAnnotations = rm.getParameterAnnotations();
for (int i = 0; i < paramAnnotations.length; i++) {
// entity parameters have no annotations.
if (paramAnnotations[i].length == 0) {
return rm.getParameterTypes()[i];
}
}
return null;
}
private void findMethodAndValidate(Object entity) {
final Method[] methods = entity.getClass().getMethods();
for (Method method: methods) {
if (method.isAnnotationPresent(PostDeserialization.class)) {
// validation method should take no parameters.
if (method.getParameterCount() != 0) {
throw new RuntimeException(
"Validation method must not have parameters.");
}
try {
method.invoke(entity);
} catch (InvocationTargetException ex) {
// if an exception happens during invocation,
// an InvocationException is thrown. We want the cause,
// expecting it to be a ValidationError.
Throwable cause = ex.getCause();
if (cause instanceof ValidationError) {
throw (ValidationError) cause;
} else {
throw new RuntimeException(ex);
}
} catch (Exception ex) {
throw new RuntimeException(
"Error calling validation method.", ex);
}
}
}
}
}
#Path("test")
public static class TestResource {
#POST
#Consumes("application/json")
public String post(Bean bean) {
return bean.value;
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(TestResource.class)
.register(ValidationErrorMapper.class)
.register(ValidationFilter.class)
.register(new ExceptionMapper<Throwable>() {
#Override
public Response toResponse(Throwable t) {
t.printStackTrace();
return Response.serverError()
.entity(t.getMessage()).build();
}
});
}
#Test
public void testValidationError() {
final Bean bean = new Bean();
bean.value = "not expected";
final Response response = target("test")
.request()
.post(Entity.json(bean));
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
assertEquals("value must be 'expected'", response.readEntity(String.class));
}
#Test
public void testNoValidationError() {
final Bean bean = new Bean();
bean.value = "expected";
final Response response = target("test")
.request()
.post(Entity.json(bean));
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
assertEquals("expected", response.readEntity(String.class));
}
}

Ignoring incoming json elements in jax-rs

I'm wondering where to put the #JsonIgnoreProperties(ignoreUnknown = true) in a Java REST API. I have the following class:
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.ObjectMapper;
#JsonIgnoreProperties(ignoreUnknown = true)
public class QueuePayload {
private String message;
private String id;
public String getId() {
return this.id;
}
public String getMessage() {
return this.message;
}
public void setId(String id) {
this.id = id;
}
public void setMessage(String message) {
this.message = message;
}
public String serialize() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(this);
}
}
Which I use in a JAX-RS servlet like this:
import javax.jms.JMSException;
import javax.naming.NamingException;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
#Path("/v1")
#Produces(MediaType.APPLICATION_JSON)
public class ServiceApiV1 {
#GET
public Response getApiRoot() {
String result = "{\"notice\" : \"It is alive!\"}";
return Response.status(Status.OK).entity(result).build();
}
#POST
#Path("/update")
#Consumes(MediaType.APPLICATION_JSON)
public Response update(UpdatePayload message) {
return Response.status(Status.OK).entity(message).build();
}
}
When I post { "message" : "Test", "id" : "one" } it works like a charm, but when I post { "message" : "Test", "id" : "one", "sneaked" : "in" } I get:
SRVE0315E: An exception occurred:
com.ibm.ws.webcontainer.webapp.WebAppErrorReport:
javax.servlet.ServletException:
org.codehaus.jackson.map.exc.UnrecognizedPropertyException:
Unrecognized field "sneaked";
(Class test.QueuePayload), not marked as ignorable
at [Source: com.ibm.ws.webcontainer.srt.SRTInputStream#b7328421; line: 1, column: 7] (through reference chain: test.QueuePayload["sneaked"])
I though #JsonIgnoreProperties(ignoreUnknown = true) was designed exactly for this use case. I also tried without the property in the servlet and the various permutations. I checked this question and made sure to have imported the same API version in both classes.
What do I miss?
Update
Change the imported classed to codehouse (see above) and ran this test):
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
QueuePayload qp = new QueuePayload();
qp.setPayload("PayLoad");
qp.setQueueId("ID-of-Q");
qp.setStatus("Some Status");
System.out.println(qp.serialize());
ObjectMapper om = new ObjectMapper();
QueuePayload qp2 = om.readValue("{\"payload\":\"PayLoad\",\"queueId\":\"ID-of-Q\",\"newstatus\":\"New Status\"}",
QueuePayload.class);
System.out.println(qp2.serialize());
}
That worked. The servlet doesn't
Problem seems to be the server uses Jackson 1.x (codehaus). You are trying to use Jackson 2.x annotations (fasterxml). They don't interact. You can tell by the exception that it's a codehaus exception, meaning the older Jackson is actual used.
You can look in the server an try and find the exact version, or you can just try and use an random 1.x version (of course in a provided scope, to there's no conflict), like this one
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
<scope>provided</scope>
</dependency>
Then use org.codehaus.jackson.annotate.JsonIgnoreProperties on the model class (not the resource class).

Spring, thymeleaf, JPA/hibernate, mysql UTF-8 characters persist in db

I have problem with persisting polish PL-pl locale characters in mysql db.
I know that is common issue, and out there in the internet is many solution to this. But I cant figure it out. I think I've tried everything.
All my web page correctly encode and display UTF-8 characters (static from html and from db)
Problem cames when I save somthing in db using form on my application. So I think it is tightly related to JDBC, JPA and hibernate.
Here is my configuration:
application.properties
#DB Properties:
# Mysql
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3300/derp?characterEncoding=UTF-8
hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
db.username=root
db.password=motorolaa835
# H2
#db.driver=org.h2.Driver
#db.url=jdbc:h2:~/sts/derp/db/derp_db
#hibernate.dialect=org.hibernate.dialect.H2Dialect
#db.username=root
#Hibernate Configuration:
hibernate.show_sql=true
hibernate.hbm2ddl.auto=update
#hibernate.hbm2ddl.auto=create
hibernate.connection.CharSet=utf8
hibernate.connection.characterEncoding=utf8
hibernate.connection.useUnicode=true
services.entitymanager.packages.to.scan=com.derp
cms.entitymanager.packages.to.scan=com.derp.cms.model
common.entitymanager.packages.to.scan=com.derp.common.model
procedure.entitymanager.packages.to.scan=com.derp.procedure.model
Initializer
package com.derp.common.init;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.DispatcherServlet;
public class Initializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(WebAppConfig.class);
ctx.register(ThymeleafConfig.class);
servletContext.addListener(new ContextLoaderListener(ctx));
ctx.setServletContext(servletContext);
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setAsyncSupported(true);
servlet.setLoadOnStartup(1);
// Allow to use Put and Delete method for REST architecture
registerCharachterEncodingFilter(servletContext);
registerHiddenFieldFilter(servletContext);
}
private void registerCharachterEncodingFilter(ServletContext aContext) {
CharacterEncodingFilter cef = new CharacterEncodingFilter();
cef.setForceEncoding(true);
cef.setEncoding("UTF-8");
aContext.addFilter("charachterEncodingFilter", cef).addMappingForUrlPatterns(null ,true, "/*");
}
private void registerHiddenFieldFilter(ServletContext aContext) {
aContext.addFilter("hiddenHttpMethodFilter", new HiddenHttpMethodFilter()).addMappingForUrlPatterns(null ,true, "/*");
}
}
WebAppConfig
package com.derp.common.init;
import java.util.Properties;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
//import com.derp.common.wicketView.HomePage;
#Configuration
#ComponentScan("com.derp")
#EnableWebMvc
#EnableTransactionManagement
#PropertySource("classpath:application.properties")
public class WebAppConfig extends WebMvcConfigurerAdapter {
private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO = "hibernate.hbm2ddl.auto";
private static final String PROPERTY_NAME_HIBERNATE_CONNECTION_CHARSET = "hibernate.connection.CharSet";
private static final String PROPERTY_NAME_HIBERNATE_CONNECTION_CHARACTERENCODING = "hibernate.connection.characterEncoding";
private static final String PROPERTY_NAME_HIBERNATE_CONNECTION_USEUNICODE = "hibernate.connection.useUnicode";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_SERVICES = "services.entitymanager.packages.to.scan";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_COMMON = "common.entitymanager.packages.to.scan";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_CMS = "cms.entitymanager.packages.to.scan";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_PROCEDURE = "procedure.entitymanager.packages.to.scan";
#Resource
private Environment env;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
return dataSource;
}
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
//sessionFactoryBean.setPackagesToScan(env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
sessionFactoryBean.setPackagesToScan(new String[] {
env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_SERVICES),
env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_COMMON),
env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_CMS),
env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN_PROCEDURE)
});
sessionFactoryBean.setHibernateProperties(hibProperties());
return sessionFactoryBean;
}
private Properties hibProperties() {
Properties properties = new Properties();
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
properties.put(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO));
properties.put(PROPERTY_NAME_HIBERNATE_CONNECTION_CHARSET, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_CONNECTION_CHARSET));
properties.put(PROPERTY_NAME_HIBERNATE_CONNECTION_CHARACTERENCODING, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_CONNECTION_CHARACTERENCODING));
properties.put(PROPERTY_NAME_HIBERNATE_CONNECTION_USEUNICODE, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_CONNECTION_USEUNICODE));
properties.put("jadira.usertype.autoRegisterUserTypes", "true");
return properties;
}
#Bean
public HibernateTransactionManager transactionManager() {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
// Simple strategy: only path extension is taken into account
configurer.favorPathExtension(true).
ignoreAcceptHeader(true).
useJaf(false).
defaultContentType(MediaType.TEXT_HTML).
mediaType("html", MediaType.TEXT_HTML).
mediaType("xml", MediaType.APPLICATION_XML).
mediaType("json", MediaType.APPLICATION_JSON);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/img/**").addResourceLocations("/WEB-INF/img/*");
registry.addResourceHandler("/css/**").addResourceLocations("/WEB-INF/css/*");
registry.addResourceHandler("/js/**").addResourceLocations("/WEB-INF/js/*");
registry.addResourceHandler("/lib/**").addResourceLocations("/WEB-INF/lib/*");
}
}
ThymeleafConfig
package com.derp.common.init;
import nz.net.ultraq.thymeleaf.LayoutDialect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
#Configuration
public class ThymeleafConfig {
#Bean
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/WEB-INF/view/");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML5");
resolver.setOrder(1);
resolver.setCacheable(false);
return resolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.addDialect(new LayoutDialect());
//templateEngine.addDialect(new SpringStandardDialect());
return templateEngine;
}
#Bean
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
}
Edit
There is no utf-8 characters when using this method on controller
controller
#RequestMapping(value="/test", method=RequestMethod.PUT)
#ResponseBody
public String ajaxTest() {
return "Characters test: ęółąśżźćń";
}
and the javasscript ajax method
$(document).ready(function() {
$('h1').click(function() {
$.ajax({
type: "PUT",
url: "/derp/procedury/test",
data: "none",
success: function (response, status, xhr) {
showNotifications(status, xhr.responseText);
},
error: function (response, status, xhr) {
showNotifications('error', JSON.stringify(response));
showNotifications('error', status);
showNotifications('error', xhr);
}
});
});
The result I get is:
Characters test: ?�???????
Please give some helpful suggestions. Thanks in advance.
I had the same problem, I've added the following filter config to my MyServletInitializer
and there's no more problem (in my case other configs don't work):
public class MyServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
....
#Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
return new Filter[]{filter};
}
....
}
Working solution:
Code to add in initializer class:
private void registerCharachterEncodingFilter(ServletContext aContext) {
CharacterEncodingFilter cef = new CharacterEncodingFilter();
cef.setForceEncoding(true);
cef.setEncoding("UTF-8");
aContext.addFilter("charachterEncodingFilter", cef).addMappingForUrlPatterns(null ,true, "/*");
}
and then call it in initializer in
onStartup method
with ServletContext:
registerCharachterEncodingFilter

Spring MVC mapping view for Google-GSON?

Does anyone know if there is a Spring MVC mapping view for Gson? I'm looking for something similar to org.springframework.web.servlet.view.json.MappingJacksonJsonView.
Ideally it would take my ModelMap and render it as JSON, respecting my renderedAttributes set in the ContentNegotiatingViewResolver declaration
We plan to use Gson extensively in the application as it seems safer and better than Jackson. That said, we're getting hung up by the need to have two different JSON libraries in order to do native JSON views.
Thanks in advance!
[cross-posted to Spring forums]
aweigold got me most of the way there, but to concretely outline a solution for Spring 3.1 Java based configuration, here's what I did.
Grab GsonHttpMessageConverter.java from the spring-android-rest-template project.
Register your GsonHttpMessageConverter with the message converters in your MVC config.
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new GsonHttpMessageConverter());
}
}
The Spring docs outline this process, but aren't crystal clear. In order to get this to work properly, I had to extend WebMvcConfigurerAdapter, and then override configureMesageConverters. After doing this, you should be able to do the following in your controller method:
#Controller
public class AppController {
#RequestMapping(value = "messages", produces = MediaType.APPLICATION_JSON_VALUE)
public List<Message> getMessages() {
// .. Get list of messages
return messages;
}
}
And voila! JSON output.
I would recommend to extend AbstractView just like the MappingJacksonJsonView does.
Personally, for JSON, I prefer to use #Responsebody, and just return the object rather than a model and view, this makes it easier to test. If you would like to use GSON for that, just create a custom HttpMessageConverter like this:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import com.vitalimages.string.StringUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.stereotype.Component;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.sql.Timestamp;
#Component
public class GSONHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private GsonBuilder gsonBuilder = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
.registerTypeAdapter(Timestamp.class, new GSONTimestampConverter());
public GSONHttpMessageConverter() {
super(new MediaType("application", "json", DEFAULT_CHARSET));
}
#Override
protected boolean supports(Class<?> clazz) {
// should not be called, since we override canRead/Write instead
throw new UnsupportedOperationException();
}
#Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return MediaType.APPLICATION_JSON.isCompatibleWith(mediaType);
}
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return MediaType.APPLICATION_JSON.isCompatibleWith(mediaType);
}
public void registerTypeAdapter(Type type, Object serializer) {
gsonBuilder.registerTypeAdapter(type, serializer);
}
#Override
protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
try {
Gson gson = gsonBuilder.create();
return gson.fromJson(StringUtils.convertStreamToString(inputMessage.getBody()), clazz);
} catch (JsonParseException e) {
throw new HttpMessageNotReadableException("Could not read JSON: " + e.getMessage(), e);
}
}
#Override
protected void writeInternal(Object o, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
Type genericType = TypeToken.get(o.getClass()).getType();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputMessage.getBody(), DEFAULT_CHARSET));
try {
// See http://code.google.com/p/google-gson/issues/detail?id=199 for details on SQLTimestamp conversion
Gson gson = gsonBuilder.create();
writer.append(gson.toJson(o, genericType));
} finally {
writer.flush();
writer.close();
}
}
}
And then add it to your converter list in your handler adapter like this:
#Bean
public HandlerAdapter handlerAdapter() {
final AnnotationMethodHandlerAdapter handlerAdapter = new AnnotationMethodHandlerAdapter();
handlerAdapter.setAlwaysUseFullPath(true);
List<HttpMessageConverter<?>> converterList = new ArrayList<HttpMessageConverter<?>>();
converterList.addAll(Arrays.asList(handlerAdapter.getMessageConverters()));
converterList.add(jibxHttpMessageConverter);
converterList.add(gsonHttpMessageConverter);
handlerAdapter.setMessageConverters(converterList.toArray(new HttpMessageConverter<?>[converterList.size()]));
return handlerAdapter;
}