com.fasterxml.jackson.databind.JsonSerializer exclude fields by key - java

I have a class which I do not have control over it's source:
public class SomeClassImpl implements SomeClass {
private SomeField someFiled; // Not serializable
... // Some other fields that are serializable
}
So this class is not fully Serializable, and I am running into StackOverflowError when I try to serialize it as json using Spring Boot as #ResponseBody.
I have two controller methods:
#ResponseBody public SomeClassImpl get();
#ResponseBody public SomeOtherClass find();
I have control over the source of SomeOtherClass which includes SomeClass as a property.
I could not figure out how to ignore a field using #JsonIgnore annotation, I probably need to to control the source.What I can do with annotations is that I can ignore SomeClass property from SomeOtherClass which does not help the first method above. So I decided to implement JsonSerializer<SomeClassImpl>:
#Override
public void serialize(SomeClassImpl someClass, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
// I need to write all fields except some
}
Or can I handle this with annotations? If yes how? Thanks.

When modifying the source code is not an option, you can use mix-in annotations to add Jackson annotations to a bean.
First define a mix-in annotation interface or class:
public interface FooMixIn {
#JsonIgnore
Object getBiz();
}
Then configure ObjectMapper to use the defined interface as a mix-in for your POJO:
ObjectMapper mapper = new ObjectMapper().addMixIn(Foo.class, FooMixIn.class);
All annotation sets that Jackson recognizes can be mixed in.
All kinds of annotations (member method, static method, field, constructor annotations) can be mixed in.
Only method (and field) name and signature are used for matching annotations: access definitions (private, protected, ...) and method implementations are ignored.
For more details, have a look at the Jackson documentation.

Related

Get outer class by member annotation

I have a class with custom annotation for one of class field:
public class Test {
#CustomAnnotation
private String name;
...
}
I just want to know if it possible to get Class<Test> by this annotation? Can't find any suitable api..
public Class<?> getOuterClass(CustomAnnotation annotation) {
...
}
#CustomAnnotation is declared as #Retention(RetentionPolicy.RUNTIME)
No, annotation does not store any data about where it was declared.
Also annotation can work just like any normal interface, so someone can implement annotation in class an make instances of it that were never used as annotations.
You need either include that information yourself, by adding parameter to annotation and then using it #CustomAnn(Test.class) or when reading annotations just remember and include that information yourself in some other object.

Extending RepositoryRestMvcConfiguration breaks Jackson LocalDateTime Serialisation

I'm attempting to extend RepositoryRestMvcConfiguration to override the uriToEntityConverter for a custom one, however doing that then causes Jackson to fail to be able to serialise/deserialise LocalDateTime from String. The code below shows my subclass.
I've also tried adding jackson-datatype-jsr310 into the pom to see if I can force support, but without my subclass it works anyway so that was mostly a dead-end.
#Configuration
#Import(RepositoryRestMvcConfiguration.class)
public class RepositoryRestMvcConfigurer extends RepositoryRestMvcConfiguration {
#Override
#ConfigurationProperties(prefix = "spring.data.rest")
public RepositoryRestConfiguration config() {
return super.config();
}
#Override
protected UriToEntityConverter uriToEntityConverter(ConversionService conversionService) {
return new OverriddenUriToEntityConverter(persistentEntities(), repositoryInvokerFactory(conversionService), repositories());
}
}
EDIT:
I've solved the problem by annotating my LocalDateTime properties to specify what serialiser and deserialiser to use, but that feels more like a voodoo ritual than a solution.
So I ended up self-solving this. To actually extended that you also need:
A class that extends RepositoryRestConfigurerAdapter
A class that returns that previous class and also implements the annotations for AutoConfigureAfter
I based mine on the RepositoryRestMvcAutoConfiguration class actually within Spring but changed it from being #ConditionalOnMissingBean(RepositoryRestMvcConfiguration.class) to #ConditionalOnBean(ExtendedRepositoryRestMvcConfiguration.class). That allowed it to properly configure the RestRepository with my overridden functions.

Spring+Jackson: How to set visibility of a class without using #JsonAutoDetect annotation

In my spring-boot application, I have a global configuration on Jackson's ObjectMapper which told Jackson not to serialize object by fields but getters:
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder()
{
return new Jackson2ObjectMapperBuilder()
{
#Override
public void configure(ObjectMapper objectMapper)
{
super.configure(objectMapper);
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.GETTER, Visibility.PUBLIC_ONLY);
objectMapper.setVisibility(PropertyAccessor.IS_GETTER, Visibility.PUBLIC_ONLY);
}
};
}
However, now I'm dealing with a special case. I need to serialize a class which is not written by myself (a class form dependnecy library). Since the class does not declared getters, Jackson will ignore those fields.
Here's how the external class look like:
public class DirectionsResult
{
public GeocodedWaypoint geocodedWaypoints[];
public DirectionsRoute routes[];
}
Although using #JsonAutoDetect annotation can customerize a class's visibility for Jackson, this does not work with external classes.
So how can I set visibility of a class without using #JsonAutoDetect annotation and also not to change the global configuration?
You should be able to use jacksons MixIn feature. With this approach you can control all the configuration of a class by another class definition of your choice.
https://github.com/FasterXML/jackson-docs/wiki/JacksonMixInAnnotations
You can also check out one of my github projects to see the use of that feature:
https://github.com/Antibrumm/jackson-antpathfilter

What's the difference between #Cacheable Method and #Cacheable Class on ehcache

Working with ehcache I noticed that #Cacheable annotation could be used on top of a class decleration or on top of a method decleration, such as;
Cacheable class:
#Cacheable
class CacheableClass{
Long l;
Integer i;
String s;
}
Cacheable method:
class ...
#Cacheable
public List<ToBeCached> getCacheableClassList()
{
...
}
If #Cacheable is on top of a class then you cannot give the name of the chache but if you declare on top of a method you can give the name of the cache declared in the configuration xml. I suppose I miss something since using #Cacheable for class declerations seem obsolute to me.
Using ehcache-spring-annotations :
#com.googlecode.ehcache.annotations.Cacheable(cacheName = "test") if we give this at type level then it gives error saying The annotation #Cacheable is disallowed for this location.
According to documents I ever read Annotation Placement :
On a method.
On an interface or
On a public method on a class
Spring recommends that you only annotate methods of concrete classes with the #Cacheable annotation, as opposed to annotating methods of interfaces.
When using proxies, you should apply the #Cacheable annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the #Cacheable annotation, no error is raised, but the annotated method does not exhibit the configured cachable settings.
Using Spring Cache
If you use #org.springframework.cache.annotation.Cacheable(value="test") where value represents the name of the cache. You can specify this at type &/or method level.
You can try this & tell whether you get error or not :-
#com.googlecode.ehcache.annotations.Cacheable(cacheName = "test")
#org.springframework.cache.annotation.Cacheable(value="")
public class PortalDatabaseAdapterImpl{
#com.googlecode.ehcache.annotations.Cacheable(cacheName="test")
#org.springframework.cache.annotation.Cacheable(value="test")
public List<PageControl> getLoginPage() {}
}
If you didn't get error then I have to update myself.
Using #Cacheable at method level means, the results of the method are cached.
Using #Cacheable at interface level is used to define custom annotations as below,
//Custom annotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
#Cacheable(value="books", key="#isbn")
public #interface SlowService {
}
The below code
#Cacheable(value="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
can be replaced with
#SlowService
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
But I have never seen applying #Cacheable annotation at Class level.

Jackson JSON library: how to instantiate a class that contains abstract fields

I want to convert a JSON string into java object, but the class of this object contains abstract fields, which Jackson can't instantiate, and doesn't produce the object. What is the easiest way to tell it about some default implementation of an abstract class, like
setDefault(AbstractAnimal.class, Cat.class);
or to decide about the implementation class based on JSON attribute name, eg. for JSON object:
{
...
cat: {...}
...
}
i would just wite:
setImpl("cat", Cat.class);
I know it's possible in Jackson to embed class information inside JSON, but I don't want to complicate the JSON format I use. I want to decide what class to use just by setting default implementation class, or by the attribute name ('cat') - like in XStream library, where you write:
xStream.alias("cat", Cat.class);
Is there a way to do so, especially in one line, or does it require some more code?
There are multiple ways; before version 1.8, simplest way is probably to do:
#JsonDeserialize(as=Cat.class)
public abstract class AbstractAnimal { ... }
as to deciding based on attribute, that is best done using #JsonTypeInfo, which does automatic embeddeding (when writing) and use of type information.
There are multiple kinds of type info (class name, logical type name), as well as inclusion mechanisms (as-included-property, as-wrapper-array, as-wrapper-object). This page: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization explains some of the concepts.
A full fledged answer with a very clear example can be found here: https://stackoverflow.com/a/30386694/584947
Jackson refers to this as Polymorphic Deserialization.
It definitely helped me with my issue. I had an abstract class that I was saving in a database and needed to unmarshal it to a concrete instance of a class (understandably).
It will show you how to properly annotate the parent abstract class and how to teach jackson how to pick among the available sub-class candidates at run-time when unmarshaling.
If you want to pollute neither your JSON with extra fields nor your classes with annotation, you can write a very simple module and deserializer that uses the default subclass you want. It is more than one line due to some boilerplate code, but it is still relatively simple.
class AnimalDeserializer extends StdDeserializer<Animal> {
public AnimalDeserializer() {
super(Animal.class);
}
public Animal deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
return jsonParser.readValueAs(Cat.class);
}
}
class AnimalModule extends SimpleModule {
{
addDeserializer(Animal.class, new AnimalDeserializer());
}
}
Then register this module for the ObjectMapper and that's it (Zoo is the container class that has an Animal field).
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new AnimalModule());
return objectMapper.readValue(json, Zoo.class);
The problem can be solved with the annotation #JsonDeserialize on the abstract class.
Refers to Jackson Exceptions Problems and Solutions for more info

Categories

Resources