Using #JsonView with Spring MVC - java

I am using the following bean definition to make my spring app talking in JSON
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
Is it possible with this message converter bean to use the #JsonView annotation?

#JsonView is already supported in the Jackson JSON Processor from v1.4 onwards.
New Edit: Updated for Jackson 1.9.12
According to the v1.8.4 documentation the function I was using writeValueUsingView is now Deprecated Use ObjectMapper.viewWriter(java.lang.Class) instead… however that has also been Deprecated Since 1.9, use writerWithView(Class) instead! (see v1.9.9 documentation)
So here is an updated example, tested with Spring 3.2.0 and Jackson 1.9.12 which simply returns {id: 1} and not the extended {name: "name"} since it is using the .writerWithView(Views.Public.class). Switching to Views.ExtendPublic.class will result in {"id":1,"name":"name"}
package com.demo.app;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.codehaus.jackson.map.annotate.JsonView;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Controller
public class DemoController {
private final ObjectMapper objectMapper = new ObjectMapper();
#RequestMapping(value="/jsonOutput")
#ResponseBody
public String myObject(HttpServletResponse response) throws IOException {
ObjectWriter objectWriter = objectMapper.writerWithView(Views.Public.class);
return objectWriter.writeValueAsString(new MyObject());
}
public static class Views {
static class Public {}
static class ExtendPublic extends Public {}
}
public class MyObject {
#JsonView(Views.Public.class) Integer id = 1;
#JsonView(Views.ExtendPublic.class) String name = "name";
}
}
Previous Edit: You need to instantiate the ObjectMapper and write out the object using a custom view as shown here, or in this example:
Define views:
class Views {
static class Public {}
static class ExtendedPublic extends PublicView {}
...
}
public class Thing {
#JsonView(Views.Public.class) Integer id;
#JsonView(Views.ExtendPublic.class) String name;
}
Use views:
private final ObjectMapper objectMapper = new ObjectMapper();
#RequestMapping(value = "/thing/{id}")
public void getThing(#PathVariable final String id, HttpServletResponse response) {
Thing thing = new Thing();
objectMapper.writeValueUsingView(response.getWriter(), thing, Views.ExtendPublic.class);
}
If you are using Jackson >= 1.7 you might find that the #JSONFilter better suits your needs.

#JsonView annotation was not supported on Spring but this issue is solved!
Follow this
Add support for Jackson serialization views
Spring MVC now supports Jackon's serialization views for rendering
different subsets of the same POJO from different controller
methods (e.g. detailed page vs summary view).
Issue: SPR-7156
This is the SPR-7156.
Status: Resolved
Description
Jackson's JSONView annotation allows the developer to control which aspects of a method are serialiazed. With the current implementation, the Jackson view writer must be used but then the content type is not available. It would be better if as part of the RequestBody annotation, a JSONView could be specified.
Available on Spring ver >= 4.1
Thank you Spring!

Related

In the context of SpringMVC, how to have web services that provide different JSON representation of a same class?

I have a data class, something like this:
public class Person {
private String name;
private Long code;
// corresponding getters and setters
}
I want to write two web services that provide two different JSON representation of Person. For example, one of them provide {"name":"foo"} but the other one {"name":"foo", "code":"123"}.
As a more complicated scenario, suppose that Person has a reference property, for example address. Address has its own properties as well and I expect that both of my web services consider this property but each of which do this in their own manner.
How should my SpringMVC views be like?
Please note that I'm new to SpringMVC. So give me a sample code beside your answer, please.
UPDATE 1: After few days, all answers push me to solve the problem in controllers or by annotating the data classes. But I want to do this in views, without any more java codes. Can I do it in JSP files or thymeleaf templates or even in .properties files?
UPDATE 2: I found json-taglib. But somehow it is left out of new upgrades. Is there any similar solution?
You're using Spring-MVC so Jackson is in charge of JSON serialize and deserializing.
In this case, you can use #JsonInclude(Include.NON_NULL) to ignore null field during serialization.
public class Person {
#JsonInclude(Include.NON_NULL)
private String name;
#JsonInclude(Include.NON_NULL)
private Long code;
// corresponding getters and setters
}
If your name or code is null then it is excluded from output JSON
So if you pass code as null, your ouput JSON will look like {"name":"foo"}
When creating JSon with Spring MVC the "view renderer", by default, is Jackson. There is no need to use things like JSP or other view technology. What you want to do, is to tell Jackson how to render an object for a given situation. Multiple options are available, but I would suggest to use projections.
An example:
#RestController
#RequestMapping(value = "person")
public class PersonController {
private final ProjectionFactory projectionFactory;
public PersonController(ProjectionFactory projectionFactory) {
this.projectionFactory = projectionFactory;
}
#GetMapping("...")
public PersonBase getPerson(..., #RequestParam(value = "view", required = false, defaultValue = "base") String view) {
...
if(view.equals("extended")) {
return projectionFactory.createProjection(PersonExtended.class, person);
} else {
return projectionFactory.createProjection(PersonBase.class, person);
}
}
}
public interface PersonBase {
String getName();
}
public interface PersonExtended extends PersonBase {
Long getCode;
}
The view layer of your application are the projection classes (put then in one package, the view package if you wish).
A Controller can choose what view to render, or you could make the result dynamic as in the example.
Your question on how to render the address could be solved with another projection like this:
public interface PersonWithAddress extends PersonExtended {
AddressProjection getAddress();
}
public interface AddressProjection {
String getStreetName();
String getZipcode();
...
}
You can look for dynamic filtering of fields using MappingJacksonValue.
The filter allows you to serialize fields that meet custom criteria.
You can check my sample code here:
package com.github.tddiaz.jsonresponsefiltering;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import jdk.nashorn.internal.objects.annotations.Getter;
import lombok.Data;
import lombok.NonNull;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#RestController
class Controller {
#GetMapping("/filter")
public ResponseEntity filter() {
final Response response = new Response("value1", "value2", "value3");
//ignore field3; will only return values of field1 and field2
final SimpleBeanPropertyFilter beanPropertyFilter = SimpleBeanPropertyFilter.filterOutAllExcept("field1", "field2");
final FilterProvider filterProvider = new SimpleFilterProvider().addFilter("responseFilter", beanPropertyFilter);
final MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(response);
mappingJacksonValue.setFilters(filterProvider);
return ResponseEntity.ok(mappingJacksonValue);
}
}
#JsonFilter("responseFilter")
#Data
class Response {
#NonNull
private String field1, field2, field3;
}
}
Use Projection for expose in diferent manners your json, and ResourceProcessor if you need to add more info to this model through projection, e.g another DB table perhaps.
Based on your use case, just call the controller of your choice from the jsp/js page ...For e.g. Let's say Admin is the user then call AdminController else call User Controller ...this can be done using a simple if/else condition...you can also look into Proxy Design Pattern but that depends on the use case
I recommend you to use JSON.stringify(value[, replacer[, space]]) function on frontend. I have given an example below. You have a write a custom function according to your requirements on the specific view.
Eg. The below example ignores null values. Here I have written editperson function which removes null values.
The function has two input parameters i.e. key and value. Write your logic according to the keys and values which you want to remove or change.
var springperson = { "name":"foo","code":null }
console.log(springperson); // person recieved from spring
function editjson(key, value){
if (value !== null) return value
}
var editperson = JSON.stringify(springperson, editjson); // String representation of person
var personjson=JSON.parse(editperson); // JSON object representation of person
console.log(personjson); // person as required by the view
Comment if you have any issues.

How to make Lombok + Gson work with Spring AOP proxies

Assuming there is a simple class Student
#Data #NoArgsConstructor #AllArgsConstructor
public class Student {
private Integer age;
private String name;
}
Add a logging aspect With Spring AOP in aop.xml
<aop:config>
<aop:aspect id="log" ref="logging">
<aop:pointcut id="selectAll" expression="execution(* com.tutorial.Student.getName(..))"/>
<aop:before pointcut-ref="selectAll" method="beforeAdvice"/>
<aop:after pointcut-ref="selectAll" method="afterAdvice"/>
</aop:aspect>
</aop:config>
<bean id="student" class="com.tutorial.Student">
<property name="name" value="Zara" />
<property name="age" value="11"/>
</bean>
excluding aspects fields
public class ExcludeAspects implements ExclusionStrategy {
#Override
public boolean shouldSkipField(FieldAttributes f) {
if(f.getName().startsWith("CGLIB$"))
return true;
return false;
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
main,note the output of the first bean is empty ("{}"):
public static void main(String[] args) {
Gson gson = new GsonBuilder().setPrettyPrinting().addSerializationExclusionStrategy(new ExcludeAspects()).create();
ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
//return "{}"
Student student = (Student) context.getBean("student");
gson.toJson(student);
//works fine
Student student2 = new Student(11,"Zara");
gson.toJson(student2);
}
Update According to the accepted answer, unProxy works for me.
Your code seems to imply that your aspects are working, i.e. before/after advices from your configuration get executed. If they don't, you have problems in other places. I am further assuming that
your aspects work as designed and you have checked that,
you are using Spring AOP, not AspectJ with load-time weaving,
somehow GSON is seeing CGLIB proxies, not the original objects underneath.
Then the problem could be that GSON - I have zero experience with it, never used it before - uses reflection in order to search for fields in the proxy class. But it will not find any as the proxy only overrides methods, but does not have fields because the latter are in the original class (parent to the proxy). If this is true, you need to configure GSON to search in the original class, not in the proxy class. Then you also would not have to exclude anything.
Update:
My educated guess above was correct.
Just because I was curious about how to get the original object from a CGLIB proxy, I looked at it in a debugger. It seems like every proxy has a public final method getTargetSource which you can invoke via reflection:
package com.tutorial;
import org.springframework.aop.TargetSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class Application {
public static void main(String[] args) throws Exception {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
Student student = (Student) context.getBean("student");
TargetSource targetSource = (TargetSource)
student
.getClass()
.getMethod("getTargetSource", null)
.invoke(student, null);
System.out.println(gson.toJson(targetSource.getTarget()));
}
}
This works for me with your code, but I did not use Lombok (which you did not mention at all, I just found out when trying to compile your code!) in the mix but manually created constructors, getters and setters just to get up and running.
Besides, you do not need the ExclusionStrategy anymore.
Console log:
{
"age": 11,
"name": "Zara"
}
BTW, Lombok is known to cause trouble in connection with AspectJ because of class naming conflicts, see my answer here. This might also affect Spring AOP.
I think that you use an unhealthy (because incompatible) mix of technologies here, if you find a solution and do not want to end up writing custom type adapters for each bean class it will be quite hacky. If you remove Lombok, at least you can switch from Spring AOP to AspectJ with LTW in order to get rid of the proxy problem. AspectJ does not use proxies, so GSON might work better with it.
Update 2:
My first update was just a quick hack during a tea break. Not being a Spring user, I had too look up the API doc first in order to find interface Advised. It contains method getTargetSource(), i.e.:
We can cast our Spring bean (AOP proxy) to Advised and thus avoid ugly reflection.
Going one step further, we can dynamically determine if a given object is an (advised) proxy or not, i.e. if you change or deactivate your aspect, the same code will still work.
package com.tutorial;
import org.springframework.aop.framework.Advised;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class Application {
public static void main(String[] args) throws Exception {
try (ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aop.xml")) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
Student student = (Student) context.getBean("student");
System.out.println(gson.toJson(unProxy(student)));
}
}
public static Object unProxy(Object object) throws Exception {
return object instanceof Advised
? ((Advised) object).getTargetSource().getTarget()
: object;
}
}
Update 3: I was curious and also installed Lombok for my IDE. Actually the sample from above does work in connection with Gson and my little unProxy(Object) method. So you are good to go. :-)
you can use #Expose annotation to ignore aop fields.
eg:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String json = gson.toJson(new Book());
public class book {
#Expose
public String name;
#Expose
public int some;
...
}
Implement ExclusionStrategy like:
#RequiredArgsConstructor
public class ExcludeListedClasses implements ExclusionStrategy {
#NonNull
private Set<Class<?>> classesToExclude;
#Override
public boolean shouldSkipField(FieldAttributes f) {
return false;
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return classesToExclude.contains(clazz);
}
}
Use like:
ExclusionStrategy es = new ExcludeListedClasses( new HashSet<Class<?>>() {{
add(Logging.class);
}} );
Gson gson = new GsonBuilder().setPrettyPrinting()
.addSerializationExclusionStrategy(es).create();
There might appear other unserializable classes also, added by aspects or so. Just add those also to the set provided when constructing ExcludeListedClasses.
The difference to Arun's answer is that this way you do not need to add #Expose annotation to each class's each field where would possible be unserializable fields.
You can also use method shouldSkipField(..) in a similar way if you want to skip serialization by field name, for example.

AWS Lambda json deserialization with jackson annotations

I'm calling an aws lambda with a json body. So the fields of the json are with different name from the ones in the POJO. So what I did is to add #JsonProperty on the fields to tell jackson what are the names in json. But for some reason it seems that it doesn't recognize them and all the fields are null. If I pass a json with the same field names as the POJO it's working. Here's my class:
public class Event implements Identifiable {
#JsonProperty("distinct_id")
private String distinctId;
#JsonProperty("user_id")
private Integer userId;
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
#JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime eventDateTime;
//Here are the getters and setters
}
If I pass
{"distinct_id":"123", "user_id":123, "dt":"2017-01-04T08:45:04+00:00"}
all the fields are null and with distinctId, userId, eventDateTime it's serializing ok with the exception that it also doesn't recognize my custom serializers/deserializers but this actually is the same problem.
My conclusion is that for some reason the aws jackson is not working with the annotations but it doesn't make sense.
So I found a way to do this. You need to implement RequestStreamHandler which gives you input and output streams which you can work with:
import com.amazonaws.services.lambda.runtime.RequestStreamHandler
public class ChartHandler implements RequestStreamHandler {
private ObjectMapper objectMapper = new ObjectMapper();
#Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
DeserializationClass deserializedInput = objectMapper.readValue(inputStream, DeserializationClass.class)
objectMapper.writeValue(outputStream, deserializedInput); //write to the outputStream what you want to return
}
}
Having the input and output streams makes you independent of the format and frameworks you use to parse it.
Take a look at this quote from AWS documentation:
You shouldn't rely on any other features of serialization frameworks such as annotations. If you need to customize the serialization behavior, you can use the raw byte stream to use your own serialization.
From: https://docs.aws.amazon.com/lambda/latest/dg/java-programming-model-req-resp.html
It sounds like you have version mismatch between annotation types, and databind (ObjectMapper): both MUST be the same major version. Specifically, Jackson 1.x annotations work with Jackson 1.x databind; and 2.x with 2.x.
Difference is visible via Java package: Jackson 1.x uses org.codehaus.jackson, whereas Jackson 2.x uses com.fasterxml.jackson. Make sure to import right annotations for ObjectMapper you use.
I had this same issue and needed MyCustomClass to be taken in and out of the Lambda Function correctly so that it can be passed through my State Machine in the Step Function without any hiccups.
Building off what Hristo Angelov posted, I was able to get a solution that worked for me and I'm posting it hoping that it will help others that were stuck like I was:
import java.io.InputStream;
import java.io.OutputStream;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
public class StaticSearchPagingLambdaFunctionHandler implements RequestStreamHandler {
LambdaLogger logger = null;
MyCustomClass myCustomClass = null;
// Register the JavaTimeModule for LocalDate conversion
ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());
#Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) {
myCustomClass = objectMapper.readValue(inputStream, MyCustomClass .class);
// ...
// Do stuff with myCustomClass
// ...
objectMapper.writeValue(outputStream, myCustomClass);
}
}
Even though the JSON string will print out differently with the ObjectMapper writing to the OutPutStream, when the next lambda function takes it in while going through the Step Function, it will still get converted to LocalDate correctly.
Make sure that in MyCustomClass your toString() method prints correctly. My toString() method looks like this:
import java.time.LocalDate;
import org.json.JSONObject;
public class SimpleSearch {
private LocalDate startDate;
private LocalDate endDate;
// ...
// Getters and Setters for the LocalDate variables
// ...
#Override
public String toString() {
return new JSONObject(this).toString();
}
public SimpleSearch() {}
}
then your JSON printouts will always look like this when it gets sent to the lambda and not that other crazy Jackson format:
{
"startDate": "2018-11-01",
"endDate": "2018-11-16"
}
Some of the Maven dependencies I used:
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180813</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9.7</version>
</dependency>
Hopefully AWS fixes the Jackson conversions to be reciprocal, to and from JSON, so that we wouldn't have to resort to these custom conversions anymore.
create getter methods for the properties and put #JsonProperty on the getter methods.

java.beans.ConstructorProperties in Jackson 2

My domain Objects are enhanced using lombok, which generates the java.beans #ConstructorProperties annotation for the constructors of immutable objects.
Now in my frontend artifact, I'd like to serialize these objects to JSON using Jackson 2.
For Jackson 1, this could be done using Jackson Extensions. Is there such a solution for Jackson 2 as well or do I have to write it myself?
My main problem is that I want to keep my domain Objects frontend agnostic, so I wouldn't like to pollute them with Jackson annotations.
And no: Java 8 parameter names is not an option, as I am stuck with Java 7 for the time being.
Sean Patrick Floyd has already written a solution, but I am posting my solution because his is proprietary. This is a Jackson module that uses an AnnotationIntrospector to make a constructor annotated with #ConstructorProperties a jackson #JsonCreator.
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.json.PackageVersion;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedConstructor;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.beans.ConstructorProperties;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
public class ConstructorPropertiesModule extends SimpleModule {
public ConstructorPropertiesModule() {
super(PackageVersion.VERSION);
}
#Override
public void setupModule(Module.SetupContext context) {
super.setupModule(context);
context.insertAnnotationIntrospector(new ConstructorPropertiesAnnotationIntrospector());
}
public static class ConstructorPropertiesAnnotationIntrospector extends NopAnnotationIntrospector {
#Override
public boolean hasCreatorAnnotation(Annotated a) {
if (!(a instanceof AnnotatedConstructor)) {
return false;
}
AnnotatedConstructor ac = (AnnotatedConstructor) a;
Constructor<?> c = ac.getAnnotated();
ConstructorProperties properties = c.getAnnotation(ConstructorProperties.class);
if (properties == null) {
return false;
}
for (int i = 0; i < ac.getParameterCount(); i++) {
final String name = properties.value()[i];
final int index = i;
JsonProperty jsonProperty = new JsonProperty() {
#Override
public String value() {
return name;
}
#Override
public boolean required() {
return false;
}
#Override
public Class<? extends Annotation> annotationType() {
return JsonProperty.class;
}
#Override
public int index() {
return index;
}
};
ac.getParameter(i).addOrOverride(jsonProperty);
}
return true;
}
}
}
The module can then be registered to an object mapper to deserialize JSON using the #ConstructorProperties annotation:
ObjectMapper m = new ObjectMapper();
m.registerModules(new ConstructorPropertiesModule());
As others stated Jackson now supports #ConstructorProperties - unfortunatelly. Because it messed up things.
The logic Jackson applies is quite unfortunate. If multiple #ConstructorProperties annotated constructor are present it will create the object via the one with most parameters. Ops. This is problem especially with Lombok which annotates all constructors with #ConstructorProperties. But anyway, this annotation is not there solely for Jackson. It makes sense to annotate every single constructor for any code inspection tool which may use this information. Lombok is right here.
Imagine following object:
#Data
#Builder
#NoArgsConstructor // for Jackson
#AllArgsConstructor // for builder
public class MyDto {
private Type1 value1 = Type1.NONE;
private Type2 value2;
}
Here Jackson will always use the all-args constructor because it is annotated with #ConstructorProperties and has most parameters.
This also means that if you set only value2 in your JSON object the value1 becomes null. Not what you would expect.
Conclusion: the current behaviour (when used with Lombok or annotate more than one constructor) doesn't allow for the easy class-level default values.
Workaround: #AllArgsConstructor(suppressConstructorProperties=true) - but this is claimed to be deprecated soon as it's present just for java 1.5 compatibility purposes.
This issue has finally been resolved in Jackson 2.7 and #ConstructorProperties are now supported out-of-the-box.
See https://github.com/FasterXML/jackson-databind/issues/905
I'm afraid you will have to write a similar wrapper for Jackson2 yourself.

Filter every JAX-RS resources of an application with a single JsonView

Jackson 2.0 allows to filter JAX-RS resources with a #JsonView.
The following example shows a way to ignore people's age in a resource response.
Unfortunately, every JAX-RS method has to be annotated with #JsonView.
public class View {
public static class Public {}
public static class Private {}
}
public class People {
#JsonView(View.Public) String name;
#JsonView(View.Private) int age;
}
#Path("/people")
public class PeopleResource {
#GET
#JsonView(View.Public)
public List<People> get() {
return peoples.get();
}
#GET
#Path("/id")
#JsonView(View.Public)
public People get(#PathParam("id") int id) {
return people.get(id);
}
}
The only way that I've found to globally filter an application resources is by using a MixIn.
public class PeopleMixIn {
#JsonIgnore int age;
}
#Provider
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
private ObjectMapper mapper = new ObjectMapper();
public ObjectMapperProvider() {
mapper.addMixInAnnotations(People.class, PeopleMixIn.class);
}
public ObjectMapper getContext(Class<?> type) { return mapper; }
}
Is there a way to configure Jackson's ObjectMapper with a JsonView? Or applying a filter to every method of a resource/application?
You could sub-class Jackson JAX-RS provider, which is piece of code that handles Resource annotations. Or you can use StreamingOutput to manually use ObjectWriter, where you can specify view to use on per-call basis.
With 2.0, the goal is to reduce state that ObjectMapper has, since changing that state can not be done in thread-safe way, whereas ObjectWriter is fully thread-safe due to immutable nature. But JAX-RS providers do not yet use ObjectWriter / ObjectReader.
Another idea: you could file a Request For Enhancement (RFE) for Jackson JAX-RS provider, to add such feature ("setDefaultView()"?). This would seem like the best solution for your use case? And implementation should be quite simple, so this could go in 2.1.0 release that should be out in a few weeks.

Categories