Using Jackson JSON Views without annotating original bean class - json

Is there any way that I can use Jackson JSON Views or something like it, without having to annotate the original bean class? I'm looking for some kind of runtime/dynamic configuration to let me do something similar.
My bean is an #Entity packaged in a JAR that may be shared by multiple projects. I'm trying to avoid touching and re-packaging the shared JAR because of UI changes in the consuming projects.
Ideally I'd like to do something like
jsonViewBuilder = createViewBuilder(View.class);
jsonViewBuilder.addProperty("property1");
jsonViewBuilder.addProperty("property2");
to replace
Bean {
#JsonView(View.class)
String property1;
#JsonView(View.class)
String property2;
}
Any ideas?
Underlying environment: Spring 3.0, Spring MVC and Glassfish 3.1.1.

How about using the Mix-In feature?
http://wiki.fasterxml.com/JacksonMixInAnnotations
http://www.cowtowncoder.com/blog/archives/2009/08/entry_305.html
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonView;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY)
.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
mapper.getSerializationConfig().addMixInAnnotations(Bar.class, BarMixIn.class);
mapper.setSerializationConfig(mapper.getSerializationConfig().withView(Expose.class));
System.out.println(mapper.writeValueAsString(new Bar()));
// output: {"b":"B"}
}
}
class Bar
{
String a = "A";
String b = "B";
}
abstract class BarMixIn
{
#JsonView(Expose.class)
String b;
}
// Used only as JsonView marker.
// Could use any existing class, like Object, instead.
class Expose {}

Related

Apache Camel Rest Custom Json Deserializer

I use Camel 2.16.0 for a Camel Rest project. I have introduced an abstract type that I need a custom deserializer to handle. This works as expected in my deserialization unit tests where I register my custom deserializer to the Objectmapper for the tests. To my understanding it is possible to register custom modules to the Jackson Objectmapper used by Camel as well (camel json).
My configuration:
...
<camelContext id="formsContext" xmlns="http://camel.apache.org/schema/spring">
...
<dataFormats>
<json id="json" library="Jackson" useList="true" unmarshalTypeName="myPackage.model.CustomDeserialized" moduleClassNames="myPackage.MyModule" />
</dataFormats>
</camelContext>
My module:
package myPackage;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class MyModule extends SimpleModule {
public MyModule() {
super();
addDeserializer(CustomDeserialized.class, new MyDeserializer());
}
}
The Camel rest configuration:
restConfiguration()
.component("servlet")
.bindingMode(RestBindingMode.json)
.dataFormatProperty("prettyPrint", "true")
.contextPath("/")
.port(8080)
.jsonDataFormat("json");
When running the service and invoking a function that utilize the objectmapper I get the exception:
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of myPackage.model.CustomDeserialized, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
Any suggestions on what is wrong with my setup?
I found this solution to the problem and used this implementation for my custom jackson dataformat:
public class JacksonDataFormatExtension extends JacksonDataFormat {
public JacksonDataFormatExtension() {
super(CustomDeserialized.class);
}
protected void doStart() throws Exception {
addModule(new MyModule());
super.doStart();
}
}

How to access protected method of Java jar file's class

I am using a Java command-line application (which is open-source) as a jar file for my jrubyonrails project. The main application is like following
public class Decoder extends Annotator {
public Decoder() {
super();
}
public static void main(String[] args) {
... // Do something that I don't want
myDesiredMethod();
... // And some other thing
}
...
}
There are many steps which I want to skip, I only want myDesiredMethod function. And it is a protected method from the parent Annotator class.
public class Annotator extends Helper {
...
protected SomeClass myDesiredMethod(boolean reMap) throws Exception { ... }
...
}
Annotator class does not have any public constructor so that I cannot:
ann = Annotator.new
It raises this error: TypeError: no public constructors for Annotator.
Then I try to implement another class which inherits Annotator in order to access myDesiredMethod. This is the jruby code I have tried so far
require 'java'
require 'decoder.jar'
java_import java.util.ArrayList
java_import java.lang.StringBuilder
module MyModule
class RuDecoder < Annotator
include_package 'com.decoder'
def self.my_method
myDesiredMethod
end
end
It returns the error: NoMethodError: undefined method 'myDesiredMethod' for MyModule::RuDecoder:Class. Seems jruby does not look for the method of the parent class.
Is there any solution in my case, I don't want to rebuild the java library to jar and manually put it into my program every time it has an update.
Turns out that I made thing over-complicated. I can call the default constructor of Annotator as following:
constructors = Annotator.java_class.declared_constructors.first
constructors.accessible = true
annotator = constructors.new_instance.to_java
And use simple call myDesiredMethod: annotator.myDesiredMethod

Filtering entity fields dynamically in Spring Data rest json Response

Hi I have a requirement to dynamically ignore entity fields in spring data rest response [I know they can be done in a static way by using #JsonIgnore annotation] ideally based on a spring security Role .The role part is still manageable but how to dynamically ignore fields in the json response is a challenge.
After some analysis and the docs I think jackson is the way to go as spring data rest does provide jackson customization via jackson modules and mixins http://docs.spring.io/spring-data/rest/docs/current/reference/html/#customizing-sdr.custom-jackson-deserialization .
So I think in jackson api it could be done via #jsonFilter and then suppling the same when the ObjectMapper write the object [more details here http://www.cowtowncoder.com/blog/archives/2011/09/entry_461.html] .
But I am not sure how this could be wired up with Spring data rest (basically the part where I acan inject the filterprovider into spring data rest objectmapper).Let me know if anyone has tried this or someone from the Spring data team has insights .
Will post an answer myself If I am able to achieve the same.
UPDATE
So I figured out that the way to implement custom filtering is through the jackson BeanSerializerModifier .Got great help from #cowtowncoder on twitter .Also helpful reference or holy grails for filtering with jackson http://www.cowtowncoder.com/blog/archives/2011/02/entry_443.html
So yes finally I was able to solve this .The trick here is to use a custom BeanSerializerModifier and register it via a Custom Module [which is the custom hook available to customize spring data rest jackson serialization],something like
setSerializerModifier( new CustomSerializerModifier()).build()));
now you can customize our BeanSerializerModifier by overriding the method changeProperties to apply your custom filter ,which basically includes and excludes BeanPropertyWriter based on your logic .sample below
List<BeanPropertyWriter> included = Lists.newArrayList();
for (BeanPropertyWriter property : beanProperties)
if (!filter.contains(property.getName()))
included.add(property);
this way you can include any logic per class or otherwise and filter properties form response in a custom manner.Hope It Helps
Also have updated my code on github do look at https://github.com/gauravbrills/SpringPlayground
This example shows how to implement a dynamic JSON transformation (filtering) in a Spring Boot REST controller. It is using AOP controller advice to change controller method output in runtime. Code on github: https://github.com/andreygrigoriev/jsonfilter
AOP Advice
#ControllerAdvice
#SuppressWarnings("unused")
public class FilterAdvice implements ResponseBodyAdvice<Object> {
#Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
String fields = ((ServletServerHttpRequest) request).getServletRequest().getParameter("fields");
return new FilterMappingJacksonValue<>(body, StringUtils.isEmpty(fields) ? new String[] {} : fields.split(","));
}
#Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
}
FilterMappingJacksonValue
public class FilterMappingJacksonValue<T> extends MappingJacksonValue {
public FilterMappingJacksonValue(final T value, final String... filters) {
super(value);
setFilters(new SimpleFilterProvider().addFilter("dynamicFilter",
filters.length > 0 ? SimpleBeanPropertyFilter.filterOutAllExcept(filters) : SimpleBeanPropertyFilter.serializeAll()));
}
}
Simple DTO
#Data
#AllArgsConstructor
#JsonFilter("dynamicFilter")
public class Book {
String name;
String author;
}
BookController
#RestController
#SuppressWarnings("unused")
public class BookController {
#GetMapping("/books")
public List<Book> books() {
List<Book> books = new ArrayList<>();
books.add(new Book("Don Quixote", "Miguel de Cervantes"));
books.add(new Book("One Hundred Years of Solitude", "Gabriel Garcia Marquez"));
return books;
}
}

GWT + EJB + MYSQL

I have got some Question concerning Serialization and persistence.
At First I have got a GWT project with Client code and a Servlet to communicate with
my EJB Project.
In the EJB project there are some Persistent Entitie Classes with references among each other and beans to manage them.
The Reference may look like this:
Object A
/ \
Object B Object C
\
Object D
Mostly there are 1:n Relationships, which i have to modelling with oneToMany or something like this..
I store them into a MYSQL Database which already work with Strings.
With Strings I haven't got Problems to transfer them from the GWT Client Side over the GWt Servlet to the EJB Bean and then into the Database and the same way back to the Client Side.
But when I try to transfer an own created Class object (POJO?) between GWT Client and EJB, I always get an Serialization Exception.
Is it because of the GWT Servlet? I read something that you have to use DTo or Value Objects? Is this correct?
or isn't there a easy way to solve this?
Please see
http://code.google.com/webtoolkit/doc/latest/DevGuideServerCommunication.html#DevGuideSerializableTypes
All classes that conform to the above specification
or implement com.google.gwt.user.client.rpc.IsSerializable can be serialized.
For example:
import com.google.gwt.user.client.rpc.IsSerializable;
import java.util.HashMap;
public class Row implements IsSerializable
{
private HashMap _row;
public Row()
{
_row = new HashMap();
}
public Row(HashMap row)
{
_row = row;
}
public Object getCellValue(String columnName)
{
return _row.get(columnName);
}
public void setCellValue(String columnName, Object value)
{
_row.put(columnName, value);
}
public HashMap getRow()
{
return _row;
}
}
In the documentation there is also the link below, I've never tried that
http://code.google.com/webtoolkit/doc/latest/DevGuideServerCommunication.html#DevGuideCustomSerialization

Struts2 JSON Plugin With Annotations

I have a Struts2 Action Class configured via annotations. All of the "normal" methods that are annotated with #Action work fine.
However, I need to add a method into the action that returns JSON.
Here is a trimmed down version of my class (dao autowired with Spring):
#Namespace("featureClass")
// define success and input actions for class here
public class FeatureClassAction extends ActionSupport {
FeatureClassDao featureClassDao;
#Autowired
public setFeatureClassDao(FeatureClassDeao featureClassDao) {
this.featureClassDao = featureClassDao;
}
List<FeatureClass> featureClasses;
// snip normal actions
#Action("/featureClassesJSON")
#JSON
public String getFeatureClassesJSON() throws Exception {
featureClasses = featureClassDao.getAll();
return SUCCESS;
}
}
Can anyone assist? If I have to go the struts.xml route, that means moving all of my other actions (which work fine) into it.
I figured I would share the answer, since anyone else with the same problem would likely also face the silence.
I created two actions: FeatureClassAction and FeatureClassJsonAction. FeatureClassAction was annotated as such:
#ParentPackage("struts-default")
#Namespace("/featureClass")
public class FeatureClassAction extends ActionSupport {
FeatureClassJsonAction is annotated like this:
#ParentPackage("json-default")
#Namespace("/featureClass")
public class FeatureClassJsonAction extends ActionSupport {
The method in the JSON Action was annotated like this:
#Action(value="featureClassesJson", results = {
#Result(name="success", type="json")
})
public String getFeatureClassesJSON() throws Exception {
Hope it helps someone.