The following is a snippet from a project based on Spring Boot 1.3. Json serialization is made via Jackson 2.6.3.
I have the following Spring MVC controller:
#RequestMapping(value = "/endpoint", method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public Results<Account> getAllAccounts() throws Exception {
return service.getAllAccounts();
}
The returned Results is as follows (getters and setters removed):
public class APIResults<T> {
private Collection<T> data;
}
The Account class is as follows (getters and setters removed):
public class Account {
#JsonView(Views.ViewA.class)
private Long id;
#JsonView(Views.ViewB.class)
private String name;
private String innerName;
}
I also have the following Json Views
public class Views {
public interface ViewA {}
public interface Publisher extends ViewB {}
}
The motive is to return different view from the same controller, based on some predict.
So I'm using AbstractMappingJacksonResponseBodyAdvice to set the view at run-time. When setting bodyContainer.setSerializationView(Views.ViewA.class), I'm getting an empty json result and not a json array of objects that only contains the id property. I suspect this is because the data property in APIResults< T > is not annotated with #JsonView, however shouldn't non annotated properties be included in all views (doc)?
Is there a way to get what i want without adding the #JsonView annotation to APIResults< T > (this is not an option).
I know i can use Jackson mixin to get the functionality i desire, however is there a way to do that using Json views ?
You were right, spring MVC Jackson2ObjectMapperBuilder has been improved to set jackson mapper feature DEFAULT_VIEW_INCLUSION to false, so not annotated property data is not serialized.
To enable the feature, use following config (XML):
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="defaultViewInclusion" value="true"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
With this config, you'll have id and innerName properties serialized when using ViewA view:
{"data":[
{"id":1,"innerName":"one"},
{"id":2,"innerName":"two"},
{"id":3,"innerName":"three"}
]}
Further, you may manage to hide innerName also, by adding some other view annotation to it.
Related
This is a Spring Hibernate implementation. I have defined a custom Type definition where I need to pass
one default value to my custom Type Definition class as below.
But the value is null please help me what am I missing here ?
#TypeDef(name = "customString", typeClass = com.mydomain.EncryptString.class)
public class employee{
private String empId;
private String empName;
#Type(type="customString")
private String passportNumber;
//setter and getters
}
public class EncryptString mplements UserType{
private String password; // inject via spring configurations
#Override
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
String encryptedPassport = rs.getString(names[0]);
System.out.println(names.length);
System.out.println("##"+password); // This null ????
return ""
}
//password getters and setters methods
}
Spring configurations
<bean id="customString"
class="com.mydomain.EncryptString">
<property name="password" value="password" />
</bean>
value of the password password in nullSafeGet() method it prints Null. How to make it
inject ? I expect Spring will load all the given default values and it instantiate values
when the EncryptString class call via Hibernate annotation.
Updating my question with findings
I have saw a example where TypeDef pass spring bean id as a parameter as below.
#TypeDef(name = "encryptedString", typeClass =
org.jasypt.hibernate.type.EncryptedStringType.class, parameters = { #Parameter(name
= "encryptorRegisteredName", value = "hibernateStringEncryptor") })
Spring configuration is
<bean id="hibernateStringEncryptor"
class="org.jasypt.hibernate.encryptor.HibernatePBEStringEncryptor">
<property name="registeredName" value="hibernateStringEncryptor" />
<property name="password" value="password" />
<property name="saltGenerator">
<bean class="org.jasypt.salt.FixedStringSaltGenerator">
<property name="salt" value="salt"/>
</bean>
</property>
hibernateStringEncryptor configuration is injected via type def passing the bean id as a
parameter. I went through the code http://www.jasypt.org/download.html but could not figure
out the way the bean get injected.
I think you need to use #Configurable annotation because Spring doesn't instantiate the custom types, Hibernate does and Spring doesn't know about those instances. This is the section in the documentation that describes the scenario. I haven't used this approach myself, but #Configurable would be the first thing I'd try.
Basically, you need to annotate your EncryptString class with #Configurable, to add spring-aspect.jar to your project's classpath, to add <context:spring-configured/> to your xml config file and to enable AspectJ weaving. The documentation gives quite some details regarding this.
I have a Java Spring 3.2 + Hibernate project.
I used jackson2 annotations (com.fasterxml.jackson.annotation) in the model, and I (guess) the spring rest controller should use jackson2 (aka com.fasterxml.jackson) when serializing the requested objects.
I configured the application with:
<!-- Use the HibernateAware mapper instead of the default -->
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="myProj.util.HibernateAwareObjectMapper">
<property name="serializationInclusion">
<value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
</property>
</bean>
</property>
</bean>
</mvc:message-converters>
The HibernateAwareObjectMapper is defined this way:
package myProj.util;
import com.fasterxml.jackson.databind.ObjectMapper;
public class HibernateAwareObjectMapper extends ObjectMapper {
private static final long serialVersionUID = -5002954669679467811L;
public HibernateAwareObjectMapper() {
Hibernate4Module hbm = new Hibernate4Module();
hbm.enable(Hibernate4Module.Feature.FORCE_LAZY_LOADING);
registerModule(hbm);
}
}
so I can state that it extends the com.fasterxml ObjectMapper (OTOH I'm not sure why it was added, since I just inherited the code from other developers).
Note that from what I know spring3.2 should use jackson2 by default.
This is mostly working fine but then I have a serialization issue which only happens with a specific service/controller. I have an object which defines a parent, containing the same object as a child. This is resulting in a serialization loop, which ends with an exception on the server side:
[...]
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150)
[...]
and an incomplete JSON sent to the client.
This is the controller code:
#RequestMapping(value = "/getReleases", method = RequestMethod.POST)
public Map<String, Object> getReleases(#RequestBody Project project) {
Map<String, Object> subProjectsMap = new HashMap<String, Object>();
List<Release> releaseList = null;
try {
releaseList = jiraService.getReleases(project);
subProjectsMap.put("success", (releaseList.size() > 0) ? true : false);
subProjectsMap.put("data", releaseList);
} catch (Exception e) {
e.printStackTrace();
}
return subProjectsMap;
}
Serialization is performed implicitly by the framework.
The question is: why is spring apparently using org.codehaus.jackson rather than com.fasterxml.jackson as I would expect? Note that the model describing the serialized object is using jackson2 annotations (in particular #JsonBackReference and #JsonIgnore), so they are possibly ignored when using jackson1, which (I think) may result in the loop issue.
After many hours spent banging my head I still don't know why this is happening. Can you provide any hint?
Well, it turned out the problem was due to the missing #ResponseBody annotation in the controller code.
#RequestMapping(value = "/getReleases", method = RequestMethod.POST)
public #ResponseBody Map<String, Object> getReleases(#RequestBody Project project) {
[...]
}
Adding the #ResponseBody annotation magically fixed the problem for me.
I realized the problem by comparing this controller code with the code from similar controllers which were not showing the issue (silly of me not doing this before), after several vain attempts at digging through the Spring code.
Thanks all for the answers!
Spring automatically configures MappingJackson2HttpMessageConverter when Jackson2 is on the classpath. However, you override this behavior by defining your own bean of type MappingJackson2HttpMessageConverter. First of all, check if it is your bean being used for serialization (e.g. via debugging). Then check its configuration. It seems the constructor of the Hibernate extension to ObjectMapper is not calling super(), which means that configuration of the default SerializerProvider and BeanSerializerFactory is missing. See http://fasterxml.github.io/jackson-databind/javadoc/2.0.0/com/fasterxml/jackson/databind/ObjectMapper.html for more details.
What is the easiest way to bind a UUID in Spring MVC, such that this works:
#RequestMapping("/MyController.myAction.mvc")
#ResponseBody
public String myAction(UUID id, String myParam)...
Using the above I currentely get the following exception:
org.springframework.beans.BeanInstantiationException:
Could not instantiate bean class [java.util.UUID]:
No default constructor found;
nested exception is java.lang.NoSuchMethodException: java.util.UUID.<init>()
There are other questions on SO that skirt around this, but none seem to answer it. I'm using Spring 3.latest (4 EA actually). I'm after the latest, simplest way to achieve this.
UUID is a class that cannot simply be instantiated. Assuming that it comes as a request parameter you should first annotate the argument with #RequestParam.
#RequestMapping("/MyController.myAction.mvc")
#ResponseBody
public String myAction(#RequestParam UUID id, String myParam)...
Now this expects a request parameter with the name id to be available in the request. The parameter will be converted to a UUID by the StringToUUIDConverter which is automatically registered by Spring.
Prior to the Spring 3.2
there was no StringToUUIDConverter so additionally you have to write and register converter by your own.
public class StringToUUIDConverter implements Converter<String, UUID> {
public UUID convert(String source) {
return UUID.fromString(source);
}
}
Hook this class up to the ConversionService and you should have UUID conversion for request parameters. (This would also work if it was a request header, basically for everything that taps into the ConversionService). You also might want to have a Converter for the other-way (UUID -> String).
Hooking it up to Spring MVC is nicely explained in the reference guide (assuming you use xml config). But in short:
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="org.company.converter.StringToUUIDConverter"/>
</set>
</property>
</bean>
The converter below is available in Spring Framework (core) since version 3.2.
org.springframework.core.convert.support.StringToUUIDConverter<String, java.util.UUID>
If its coming as a Header parameter use
#RequestHeader(value="UUID") String id
If its coming in a model
#ModelAttribute(value="ModelName") Entity modelName
I have a class extending the AbstractExcelView class of Spring which renders an XML file. Within this class, I am injecting my Service bean for use. I am autowiring and component scanning my classes, and I would like to also do the same with this view class, but I am not clear how (or if it can be done). Here's what I'm trying to annotate from the config:
<bean id="export.xls" class="com.my.views.ReportExcelView">
<property name="url">
<value>/excel/template</value>
</property>
<property name="service" ref="testingService"/>
I am able to annotate the class with #Component, and the service with #Autowired, but I don't know of a strategy to annotate the URL. What I'd really like to do is condition it within the buildExcelWorkbook() call (based on something in the request), but it seems there is some initialization done before this, as I get an error trying to use my excel template with this method that indicates it does not have a handle to the Excel sheet. Any recommendations?
So your ReportExcelView probably looks like this right now. Make sure you use #Resource to wire a simple String.
package com.ex.springbasicex.view;
#Component
public class ReportExcelView{
#Resource(name="myUrl")
String url;
#Autowired
Service service;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
Your context config probably should be like what is below using scanning. Below is how to set the myUrl String resource.
<context:component-scan base-package="com.ex.springbasicex.view" />
<bean id="myUrl" class="java.lang.String" >
<constructor-arg>
<value>/excel/template</value>
</constructor-arg>
</bean>
I'm trying to do some spring validation with the error messages in properties files.
But the examples I find all seem to have the values hardcoded, or gotten from a properties file but using a validator class and retrieving it there.
My setup is a bit different.
I'm using the #Valid annotation in my requestmapping, and my #Valid class uses #NotNull etc.
I've seen some examples where people do #NotNull(message = "blablabla");
But that's also hardcoded, and I'd like to put the messages in a properties file so I can easily edit it on the fly and so I can easily implement i18n in the future.
Any input on how to achieve this would be appreciated.
It works exactly the same way as with explicit Validator - you declare a MessageSource and write error messages in .properties files. Messages codes are formed as constraintName.modelAttributeName.propertyName:
publib class Foo {
#NotNull private String name;
...
}
.
#RequestMapping
public String submitFoo(#Valid Foo foo, ...) { ... }
messages.properties:
NotNull.foo.name=...
MessageSource declaration:
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value = "messages" />
</bean>