spring framework spring-data-redis serialization exception - java

I am very novice to Java programming. I am C# developer. I was working on one of the small project where we want to use the redis cache instead of ehcache.
We are using spring.data.redis version 2.2.3.RELEASE and redis.client.jedis version 3.2.0.
Here is my redisconf class which contains RedisTemplate as well:
public class RedisConf {
private String REDIS_HOSTNAME = "localhost";
private int REDIS_PORT = 6379;
protected JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(REDIS_HOSTNAME, REDIS_PORT);
JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder().usePooling().build();
JedisConnectionFactory factory = new JedisConnectionFactory(configuration,jedisClientConfiguration);
factory.afterPropertiesSet();
return factory;
}
public RedisTemplate<String,Element> redisTemplate() {
final RedisTemplate<String,Element> redisTemplate = new RedisTemplate<String,Element>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new GenericToStringSerializer<Object>(Object.class));
redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setConnectionFactory(jedisConnectionFactory());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
Here is how instantiate RedisConf class:
RedisConf rf = new RedisConf();
RedisTemplate<String, Element> redisTemplate = rf.redisTemplate();
redisTemplate.getConnectionFactory().getConnection().ping();
cache = new RedisDelegate(redisTemplate);
Here is my RedisDeligate class where I do all my get and put operation:
public class RedisDelegate implements Cache {
private final RedisTemplate<String,Element> redisTemplate;
private final Logger LOGGER = LoggerFactory.getLogger(RedisDelegate.class);
private static String REDIS_KEY = "Redis";
public RedisDelegate(RedisTemplate<String,Element> redisTemplate) {
this.redisTemplate = redisTemplate;
}
#Override
public String getName() {
return "my-redis";
}
#Override
public Object getNativeCache() {
return this.redisTemplate;
}
#Override
public Element get(Object key) {
try {
LOGGER.debug("Key is: {}", key);
Object element = this.redisTemplate.opsForHash().get(REDIS_KEY, key);
LOGGER.debug("element Object is: {}", element);
return (element == null) ? null : (Element) element;
//return (Element) this.redisTemplate.opsForHash().get(REDIS_KEY, key);
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
return null;
}
#Override
public void put(Element element) {
this.redisTemplate.opsForHash().put(REDIS_KEY, element.key(), element);
}
#Override
public void evict(Object key) {
this.redisTemplate.opsForHash().delete(REDIS_KEY, key);
}
#Override
public void clear() {
redisTemplate.execute((RedisCallback<Object>) connection -> {
connection.flushDb();
return null;
});
}
}
Here is CacheElement Object that I am trying to put in the cache which implements Serializable as seen below:
public class CacheElement implements Element, Serializable {
.
.
.
}
When I try to put an object into cache, it fails with an error that serialization failed. I know there is some problem with my RedisTemplate but I can't seen to figure out. Here is the error I am receiving:
Cannot serialize; nested exception is
org.springframework.core.serializer.support.SerializationFailedException:
Failed to serialize object using DefaultSerializer; nested exception
is java.lang.IllegalArgumentException: DefaultSerializer requires a
Serializable payload but received an object of type
[io.gravitee.policy.cache.resource.CacheElement]
EDIT
I am trying to store the instance of CacheElement class into Redis:
public class CacheElement implements Element {
private final String key;
private final CacheResponse response;
private int timeToLive = 0;
public CacheElement(String key, CacheResponse response) {
this.key = key;
this.response = response;
}
public int getTimeToLive() {
return timeToLive;
}
public void setTimeToLive(int timeToLive) {
this.timeToLive = timeToLive;
}
#Override
public Object key() {
return key;
}
#Override
public Object value() {
return response;
}
#Override
public int timeToLive() {
return timeToLive;
}
}
CacheResponse object contains Buffer.
public class CacheResponse {
private int status;
private HttpHeaders headers;
private Buffer content;
public Buffer getContent() {
return content;
}
public void setContent(Buffer content) {
this.content = content;
}
public HttpHeaders getHeaders() {
return headers;
}
public void setHeaders(HttpHeaders headers) {
this.headers = headers;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
}
I would like to serialize this CacheElement object and store it in Redis.
I also would like to deserialize it on get operation.
I will appreciate help fixing this issue. As I mentioned, I am not a Java developer. I am coming from C# and visual studio world.
Thanks

Please implement your paypload object class with Serializable interface

Related

How can I change a converter for testing purpose?

I am currently working on a project and have to deal with some test cases. We implementented cryptographie in our database to learn how it is build but now we have to update the tests. The crypto should be hidden or someway.
This is the converter we used and added #Convert in the entity classes like so
#Column(name ="name", nullable = false)
#Convert(converter = DBEncryptedStringConverter.class)
public EncryptedString getName() {
return this.name;
}
public void setName(EncryptedString name) {
this.name = name;
}
#Converter
#Slf4j
public class DBEncryptedStringConverter implements AttributeConverter<EncryptedString, String> {
private static final String ENC = "ENC__##__", RAW = "RAW__##__";
#Override
public String convertToDatabaseColumn(EncryptedString attribute) {
if(attribute == null) return null;
return (attribute.isEncrypted() ? ENC : RAW) + attribute.getString();
}
#Override
public EncryptedString convertToEntityAttribute(String dbData) {
if(dbData == null) return null;
if(dbData.startsWith(ENC)) {
return new EncryptedString(dbData.substring(ENC.length()), true);
} else if(dbData.startsWith(RAW)) {
return new EncryptedString(dbData.substring(RAW.length()), false);
} else {
log.warn("DB Entry without prefix found");
log.warn("Treating as RAW");
return new EncryptedString(dbData, false);
}
}
}
I would like to use this class for testing
#Converter
public class MockDBEncryptedStringConverter implements AttributeConverter<EncryptedString, String> {
#Override
public String convertToDatabaseColumn(EncryptedString attribute) {
return attribute.getString();
}
#Override
public EncryptedString convertToEntityAttribute(String dbData) {
return new EncryptedString(dbData, false);
}
}
I think mokcing with mokito would be a way to go, but I dont quiet understand how to use it

How to access internal json object in Spring Rest Request

I want to create one API which format will be like below.
{
"jsonObject": {
//some json object
},
"key": "SampleKey",
"jsonDataKey": "SampleDataKey"
}
for this I have created the RequestBody class as below.
public class ParentJsonInfo {
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
private String key;
public JsonObject getJsonData() {
return jsonData;
}
public void setJsonData(JsonObject jsonData) {
this.jsonData = jsonData;
}
private JsonObject jsonData;
public String getJsonDataKey() {
return jsonDataKey;
}
public void setJsonDataKey(String jsonDataKey) {
this.jsonDataKey = jsonDataKey;
}
private String jsonDataKey;
}
but unfortunately I am not getting any data inside the json object of my class. M I doing anything wrong. please guide me to how should i access the data inside that object.
Here is the controller method code.
#RequestMapping(value = "/postNews", method = RequestMethod.POST)
public Greeting greeting(#RequestBody ParentJsonInfo parentJsonInfo) {
Jsonobject jsonObject= parentJsonInfo.getjsonObject();
}
The problem you are having is that you are trying to deserialize jsonObject which is from your json, but your field is called jsonData.
As #Mushtu mentioned, you need to rename that field.
Here is your ParentJsonInfo with a few adjustments:
moving the fields to the top (it is a good practice to group fields and methods separately)
renamed your field from jsonData to jsonObject
ParentJsonInfo:
public class ParentJsonInfo {
private String key;
private JsonObject jsonObject;
private String jsonDataKey;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public JsonObject getJsonObject() {
return jsonObject;
}
public void setJsonObject(JsonObject jsonObject) {
this.jsonObject = jsonObject;
}
public String getJsonDataKey() {
return jsonDataKey;
}
public void setJsonDataKey(String jsonDataKey) {
this.jsonDataKey = jsonDataKey;
}
}
JsonObject:
public class JsonObject {
private Map<String, Object> other = new HashMap<>();
#JsonAnyGetter
public Map<String, Object> getProperties() {
return other;
}
#JsonAnySetter
public void set(String name, String value) {
other.put(name, value);
}
}
u can modify like this
public Greeting greeting(#RequestBody String parentJsonInfo) {
// parse string to jsonobject
}

Custom Jackson Deserialization of a Generic Abstract class

I am having issues when trying to deserializing the following class:
public class MetricValuesDto {
private Map<MetricType, MetricValueDto<?>> metricValues;
public MetricValuesDto() {
}
public MetricValuesDto(Map<MetricType, MetricValueDto<?>> metricValues) {
this.metricValues = metricValues;
}
public Map<MetricType, MetricValueDto<?>> getMetricValues() {
return metricValues;
}
public void setMetricValues(Map<MetricType, MetricValueDto<?>> metricValues) {
this.metricValues = metricValues;
}
}
My generic abstract class:
public abstract class MetricValueDto<T> {
private T value;
private MetricTrend trend;
public MetricValueDto(T value, MetricTrend trend) {
this.value = value;
this.trend = trend;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public MetricTrend getTrend() {
return trend;
}
public void setTrend(MetricTrend trend) {
this.trend = trend;
}
}
I have two concrete classes which implement MetricValueDto:
IntMetricValueDto:
public class IntMetricValueDto extends MetricValueDto<Integer> {
public IntMetricValueDto(Integer value, MetricTrend trend) {
super(value, trend);
}
}
FloatMetricValueDto:
public class FloatMetricValueDto extends MetricValueDto<Float> {
public FloatMetricValueDto(Float value, MetricTrend trend) {
super(value, trend);
}
}
Any idea of what's the correct strategy to deserialize MetricValueDto so I can parse it through ObjectMapper or an RestTemplate? Whenever I run:
restTemplate.exchange("myEndpoint", HttpMethod.GET, entity, DataCollectionEventDto.class);
I get
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.resson.dto.MetricValueDto: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
DataCollectionEventDto:
public class DataCollectionEventDto {
private List<MapLayerDto> mapLayers;
#JsonUnwrapped
private MetricValuesDto metricValues;
public List<MapLayerDto> getMapLayers() {
return mapLayers;
}
public void setMapLayers(List<MapLayerDto> mapLayers) {
this.mapLayers = mapLayers;
}
public MetricValuesDto getMetricValues() {
return metricValues;
}
public void setMetricValues(MetricValuesDto metricValues) {
this.metricValues = metricValues;
}
#Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
I have basically tried everything on web and I could not make it work; any suggestion would be helpful.
Use JsonSubTypes annotation with JsonTypeInfo to indicate subtypes. The property attribute JsonTypeInfo is used to differentiate between different subclasses.
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "typ")
#JsonSubTypes({
#JsonSubTypes.Type(value = IntMetricValueDto.class, name = "INT"),
#JsonSubTypes.Type(value = FloatMetricValueDto.class, name = "FLT")})
public abstract class MetricValueDto<T> {
private T value;
private MetricTrend trend;
...
}
While JsonTypeInfo works, and adds implementation-specific detail to the response, which later might add confusion to the API client.
I ended up implementing a custom StdDeserializer:
public class MetricValueDtoDeserializer<T> extends StdDeserializer<MetricValueDto<T>> {
private static final long serialVersionUID = 1L;
public MetricValueDtoDeserializer() {
this(null);
}
public MetricValueDtoDeserializer(Class<?> vc) {
super(vc);
}
private ObjectMapper mapper;
#Override
public MetricValueDto<T> deserialize(JsonParser jsonParser, DeserializationContext context)
throws IOException, JsonProcessingException {
String metricType = jsonParser.getCurrentName();
mapper = (ObjectMapper) jsonParser.getCodec();
ObjectNode objectNode = (ObjectNode) mapper.readTree(jsonParser);
Iterator<Entry<String, JsonNode>> elementsIterator = objectNode.fields();
Number number = null;
while (elementsIterator.hasNext()) {
Entry<String, JsonNode> element = elementsIterator.next();
String key = element.getKey();
if (key.equals("value")) {
number = parseValue(element, metricType);
}
if (key.equals("trend")) {
MetricTrend metricTrend = parseTrend(element);
return (produceMetricValueDto(number, metricTrend));
}
}
throw new IOException();
}
#SuppressWarnings("unchecked")
private MetricValueDto<T> produceMetricValueDto(Number number, MetricTrend metricTrend) throws IOException {
if (number instanceof Integer) {
return (MetricValueDto<T>) new IntMetricValueDto((Integer) number, metricTrend);
} else if (number instanceof Float) {
return (MetricValueDto<T>) new FloatMetricValueDto((Float) number, metricTrend);
}
throw new IOException();
}
private MetricTrend parseTrend(Entry<String, JsonNode> element)
throws JsonProcessingException {
String trend = mapper.treeToValue(element.getValue(), String.class);
if (trend == null) {
return null;
} else {
return MetricTrend.valueOf(trend);
}
}
private Number parseValue(Entry<String, JsonNode> element, String metricType)
throws IOException {
if (metricType.equals(MetricType.CANOPY_COVERAGE.toValue())
|| metricType.equals(MetricType.PLANT_SIZE.toValue())) {
return mapper.treeToValue(element.getValue(), Float.class);
} else if (metricType.equals(MetricType.INSECT_COUNT.toValue())
|| metricType.equals(MetricType.PLANT_COUNT.toValue())) {
return mapper.treeToValue(element.getValue(), Integer.class);
}
throw new IOException();
}
}
The code ended up to being more complex than JsonTypeInfo, but the API client is unaware of implementation-specific details.

DynamoDB mapping List of Enum

Mapping an enum class in to DynamoDB object is really simple by using Custom Marshall. But how to map a List of Enum?
Enum class
public enum Transport {
SMS,EMAIL,ALL;
}
DynamoDB mapper
public class Campaign{
private List<Transport> transport;
#DynamoDBAttribute(attributeName = "transport")
public List<Transport> getTransport() {
return transport;
}
public void setTransport(List<Transport> transport) {
this.transport = transport;
}
}
DynamoDBMarshaller is deprecated.
Use DynamoDBTypeConverter instead.
Example:
Enum class
public static enum ValidationFailure {
FRAUD, GENERAL_ERROR
}
DynamoDBTable class
#DynamoDBTable(tableName = "receipt")
public class Receipt {
private Long id;
private List<ValidationFailure> validation;
#DynamoDBHashKey(attributeName = "id")
public Long getId() {
return id;
}
#DynamoDBTypeConverted(converter = ValidationConverter.class)
public List<ValidationFailure> getValidation() {
return validation;
}
public void setId(Long id) {
this.id = id;
}
public void setValidation(List<ValidationFailure> validation) {
this.validation = validation;
}
}
Convertor:
public class ValidationConverter implements DynamoDBTypeConverter<List<String>, List<ValidationFailure>> {
#Override
public List<String> convert(List<ValidationFailure> object) {
List<String> result = new ArrayList<String>();
if (object != null) {
object.stream().forEach(e -> result.add(e.name()));
}
return result;
}
#Override
public List<ValidationFailure> unconvert(List<String> object) {
List<ValidationFailure> result = new ArrayList<ValidationFailure>();
if (object != null) {
object.stream().forEach(e -> result.add(ValidationFailure.valueOf(e)));
}
return result;
}
}
It's working for me, I have used the Set
#DynamoDBTyped(DynamoDBMapperFieldModel.DynamoDBAttributeType.SS)
var roles: MutableSet<Employee.Role>? = null
I think the same approach would work for List with DynamoDBAttributeType.L
I found the answer myself. I create a custom marshall like below.
public class TransportMarshaller implements DynamoDBMarshaller<List<Transport>> {
#Override
public String marshall(List<Transport> transports) {
List<String>transportMap=new ArrayList<>();
for(Transport transport:transports){
transportMap.add(transport.name());
}
return transportMap.toString().replaceAll("\\[|\\]", "");//Save as comma separate value for the purpose of easiness to unmarshall
}
#Override
public List<Transport> unmarshall(Class<List<Transport>> aClass, String s) {
List<String>map= Arrays.asList(s.split("\\s*,\\s*")); //split from comma and parse to List
List<Transport>transports=new ArrayList<>();
for (String st:map){
transports.add(Transport.valueOf(st));
}
return transports;
}
}

Select JsonView in the Spring MVC Controller

I'm currently writing a REST api using Jackson (2.4.0-rc3) and spring mvc (4.0.3), and I'm trying to make it secure.
In this way, I try to use JsonView to select the parts of the objects that can be serialized.
I've found the solution (which is not for me) to annotate my Controller method with the view I want. But I'd like to select on the fly the view inside the controller.
Is it possible to extend the ResponseEntity class in order to specify which JsonView I want ?
A little piece of code :
Here is the account class
public class Account {
#JsonProperty(value = "account_id")
private Long accountId;
#JsonProperty(value = "mail_address")
private String mailAddress;
#JsonProperty(value = "password")
private String password;
#JsonProperty(value = "insert_event")
private Date insertEvent;
#JsonProperty(value = "update_event")
private Date updateEvent;
#JsonProperty(value = "delete_event")
private Date deleteEvent;
#JsonView(value = PublicView.class)
public Long getAccountId() {
return accountId;
}
#JsonView(value = PublicView.class)
public void setAccountId(Long accountId) {
this.accountId = accountId;
}
#JsonView(value = OwnerView.class)
public String getMailAddress() {
return mailAddress;
}
#JsonView(value = OwnerView.class)
public void setMailAddress(String mailAddress) {
this.mailAddress = mailAddress;
}
#JsonIgnore
public String getPassword() {
return password;
}
#JsonView(value = OwnerView.class)
public void setPassword(String password) {
this.password = password;
}
#JsonView(value = AdminView.class)
public Date getInsertEvent() {
return insertEvent;
}
#JsonView(value = AdminView.class)
public void setInsertEvent(Date insertEvent) {
this.insertEvent = insertEvent;
}
#JsonView(value = AdminView.class)
public Date getUpdateEvent() {
return updateEvent;
}
#JsonView(value = AdminView.class)
public void setUpdateEvent(Date updateEvent) {
this.updateEvent = updateEvent;
}
#JsonView(value = AdminView.class)
public Date getDeleteEvent() {
return deleteEvent;
}
#JsonView(value = OwnerView.class)
public void setDeleteEvent(Date deleteEvent) {
this.deleteEvent = deleteEvent;
}
#JsonProperty(value = "name")
public abstract String getName();
}
Here is the account controller
#RestController
#RequestMapping("/account")
public class AccountCtrlImpl implements AccountCtrl {
#Autowired
private AccountSrv accountSrv;
public AccountSrv getAccountSrv() {
return accountSrv;
}
public void setAccountSrv(AccountSrv accountSrv) {
this.accountSrv = accountSrv;
}
#Override
#RequestMapping(value = "/get_by_id/{accountId}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<Account> getById(#PathVariable(value = "accountId") Long accountId) {
try {
return new ResponseEntity<Account>(this.getAccountSrv().getById(accountId), HttpStatus.OK);
} catch (ServiceException e) {
return new ResponseEntity<Account>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#Override
#RequestMapping(value = "/get_by_mail_address/{mail_address}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<Account> getByMailAddress(#PathVariable(value = "mail_address") String mailAddress) {
try {
return new ResponseEntity<Account>(this.getAccountSrv().getByMailAddress(mailAddress), HttpStatus.OK);
} catch (ServiceException e) {
return new ResponseEntity<Account>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#Override
#RequestMapping(value = "/authenticate/{mail_address}/{password}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<Account> authenticate(#PathVariable(value = "mail_address") String mailAddress, #PathVariable(value = "password") String password) {
return new ResponseEntity<Account>(HttpStatus.NOT_IMPLEMENTED);
}
}
I really like the solution presented here to dynamically select a json view inside your controller method.
Basically, you return a MappingJacksonValue which you construct with the value you want to return. After that you call setSerializationView(viewClass) with the proper view class. In my use case, I returned a different view depending on the current user, something like this:
#RequestMapping("/foos")
public MappingJacksonValue getFoo(#AuthenticationPrincipal UserDetails userDetails ) {
MappingJacksonValue value = new MappingJacksonValue( fooService.getAll() );
if( userDetails.isAdminUser() ) {
value.setSerializationView( Views.AdminView.class );
} else {
value.setSerializationView( Views.UserView.class );
}
return value;
}
BTW: If you are using Spring Boot, you can control if properties that have no view associated are serialized or not by setting this in your application.properties:
spring.jackson.mapper.default_view_inclusion=true
I've solved my problem extending ResponseEntity like this :
public class ResponseViewEntity<T> extends ResponseEntity<ContainerViewEntity<T>> {
private Class<? extends BaseView> view;
public ResponseViewEntity(HttpStatus statusCode) {
super(statusCode);
}
public ResponseViewEntity(T body, HttpStatus statusCode) {
super(new ContainerViewEntity<T>(body, BaseView.class), statusCode);
}
public ResponseViewEntity(T body, Class<? extends BaseView> view, HttpStatus statusCode) {
super(new ContainerViewEntity<T>(body, view), statusCode);
}
}
and ContainerViewEntity encapsulate the object and the selected view
public class ContainerViewEntity<T> {
private final T object;
private final Class<? extends BaseView> view;
public ContainerViewEntity(T object, Class<? extends BaseView> view) {
this.object = object;
this.view = view;
}
public T getObject() {
return object;
}
public Class<? extends BaseView> getView() {
return view;
}
public boolean hasView() {
return this.getView() != null;
}
}
After that, we have convert only the object with the good view.
public class JsonViewMessageConverter extends MappingJackson2HttpMessageConverter {
#Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
if (object instanceof ContainerViewEntity && ((ContainerViewEntity) object).hasView()) {
writeView((ContainerViewEntity) object, outputMessage);
} else {
super.writeInternal(object, outputMessage);
}
}
protected void writeView(ContainerViewEntity view, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
JsonEncoding encoding = this.getJsonEncoding(outputMessage.getHeaders().getContentType());
ObjectWriter writer = this.getWriterForView(view.getView());
JsonGenerator jsonGenerator = writer.getFactory().createGenerator(outputMessage.getBody(), encoding);
try {
writer.writeValue(jsonGenerator, view.getObject());
} catch (IOException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
private ObjectWriter getWriterForView(Class<?> view) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
return mapper.writer().withView(view);
}
}
And to finish, I enable the converter
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="wc.handler.view.JsonViewMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
And that's it, I can select the View in the controller
#Override
#RequestMapping(value = "/get_by_id/{accountId}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseViewEntity<Account> getById(#PathVariable(value = "accountId") Long accountId) throws ServiceException {
return new ResponseViewEntity<Account>(this.getAccountSrv().getById(accountId), PublicView.class, HttpStatus.OK);
}
FYI, Spring 4.1 already supported using #JsonView directly on #ResponseBody and ResponseEntity:
Jackson’s #JsonView is supported directly on #ResponseBody and ResponseEntity controller methods for serializing different amounts of detail for the same POJO (e.g. summary vs. detail page). This is also supported with View-based rendering by adding the serialization view type as a model attribute under a special key.
And in http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-jsonview you can find the much simpler solution:
#RestController
public class UserController {
#RequestMapping(value = "/user", method = RequestMethod.GET)
#JsonView(User.WithoutPasswordView.class)
public User getUser() {
return new User("eric", "7!jd#h23");
}
}
public class User {
public interface WithoutPasswordView {};
public interface WithPasswordView extends WithoutPasswordView {};
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
#JsonView(WithoutPasswordView.class)
public String getUsername() {
return this.username;
}
#JsonView(WithPasswordView.class)
public String getPassword() {
return this.password;
}
}
This works great :
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public void getZone(#PathVariable long id, #RequestParam(name = "tree", required = false) boolean withChildren, HttpServletResponse response) throws IOException {
LOGGER.debug("Get a specific zone with id {}", id);
Zone zone = zoneService.findById(id);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
if (withChildren) {
response.getWriter().append(mapper.writeValueAsString(zone));
} else {
response.getWriter().append(mapper.writerWithView(View.ZoneWithoutChildren.class).writeValueAsString(zone));
}
}

Categories