#JsonInclude(Include.NON_NULL) not working/ jackson serializing null values - java

I have placed the annotation over the class/pojo and also configured the mapper, but it still serialize null values
I am using Hibernate 4.3.7Final and Jackson 2.4.4. The collections are lazy loaded
Pojo : Removed getter and setters
#JsonInclude(Include.NON_NULL)
#Entity
#Table
public class School {
#Id
#GeneratedValue
private int id;
#OneToMany(cascade=CascadeType.ALL,fetch= FetchType.LAZY)
private List<Student> students;
#OneToMany(cascade=CascadeType.ALL,fetch= FetchType.LAZY)
private List<Employee> staff;
}
JSONMapper:
#Component
public class JSONMapper extends ObjectMapper {
/**
*
*/
private static final long serialVersionUID = -3131980955975958812L;
//ref http://blog.pastelstudios.com/2012/03/12/spring-3-1-hibernate-4-jackson-module-hibernate/
public JSONMapper() {
Hibernate4Module hm = new Hibernate4Module();
registerModule(hm);
configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
configure(SerializationFeature.INDENT_OUTPUT , false);
configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
setSerializationInclusion(Include.NON_NULL);
}
}
Output :
{"id":1,"students":null,"staff":null}

Try using JsonInclude.NON_EMPTY instead.

You may want to file a bug against project; it could be that handling for lazy-loaded collections (which do require special handling and overrides to default one) is not doing proper inclusion checks.

Related

Include/exclude Attributes in json response from application.yml

I am using JHipster(spring boot) to generate my project. I would like to hide/show fields in JSON from application.yml. for exemple:
I have the following class
#Entity
#Table(name = "port")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Port implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
#Column(name = "id")
private Long id;
#Column(name = "city")
private String city;
#Column(name = "description")
private String description;
//getters & setters
}
My GET method return a response like:
{
"id": 1,
"city": "boston",
"description": "test test"
}
I would like to be able to include/exclude some fields from application.yml (since i don't have application.properties) otherwise to have something like:
//application.yml
include: ['city']
exclude: ['description']
in this exemple my json should look like:
{
"id": 1,
"city": "boston",
}
for exemple if I have 40 fields and I need to hide 10 and show 30 I just want to put the 10 I want to hide in exclude in application.yml without go everytime to change the code. I guess #jsonignore hide fields but I don't know how to do it from application.yml
Sorry for not explaining well. I hope it's clear.
Thank you in advance for any suggestion or solution to do something similar
Spring boot by default uses Jackson JSON library to serialize your classes to Json. In that library there is an annotation #JsonIgnore which is used precisely for the purpose to tell Json engine to egnore a particular property from serialization/de-serialization. So, lets say in your entity Port you would want to exclude property city from showing. All you have to do is to annotate that property (or its getter method) with #JsonIgnore annotation:
#Column(name = "city")
#JsonIgnore
private String city;
You can try to create a hashmap in your controller to manage your HTTP response.
Map<String, Object> map = new HashMap<>();
map.put("id", Port.getId());
map.put("city", Port.getCity());
return map;
Basically you don't expose your Port entity in your REST controller, you expose a DTO (Data Transfer Object) that you value from your entity in service layer using a simple class (e.g PortMapper). PortDTO could also be a Map as suggested in other answer.
Your service layer can then use a configuration object (e.g. PortMapperConfiguration) that is valued from application.yml and used by PortMapper to conditionally call PortDTO setters from Port getters.
#ConfigurationProperties(prefix = "mapper", ignoreUnknownFields = false)
public class PortMapperConfiguration {
private List<String> include;
private List<String> exclude;
// getters/setters
}
#Service
public class PortMapper {
private PortMapperConfiguration configuration;
public PortMapper(PortMapperConfiguration configuration) {
this.configuration = configuration;
}
public PortDTO toDto(Port port) {
PortDTO dto = new PortDTO();
// Fill DTO based on configuration
return dto;
}
}

How to mapping an entity to multiple mongodb collection in Spring Data MongoDB with JPA without use MongoTempalte directly?

I'm using org.mongodb:bson:4.1.2 with org.springframework.boot:spring-boot-starter-data-mongodb:2.4.7.
My entity looks like:
#Entity
#Table(name = "fire_alert")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
#Document(collection = "alert_<dynamic>")
#Data
public class AlertPO implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "owner_id")
private Long ownerId;
#Column(name = "alert_type")
private Long alertType;
}
Cause there will be millions of alerts, so I need to save records into different mongodb collections based on AlertPO.alertType.
After digging into org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity, I found the field collection of annotation #Document support SpEL expression. This kind of expressions will be evaluated in org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity#getCollection and it is obviously that current entity won't be added into the EvaluationContext.
According to this question:
How to Map a Java Entity to Multiple MongoDB Collections in Spring Data?
We can overwrite repositories to use MongoTemplate to persistent data into proper collection programmatically. But we're going to using JPA and we do not want to using MongoTemplate directly. How to do this?
Although the SpEL expression's EvaluationContext contains no instances of AlertPO, but we can make it possible through ThreadLocal instances registered in BeanFactory.
Such as:
#Component(value = "collection")
public class CollectionHolder {
private static final ThreadLocal<Stack<String>> COLLECTION = ThreadLocal.withInitial(Stack::new);
public void push(Alert alert) {
COLLECTION.get().push(typeToCollection(alert.getAlertType()));
}
public void push(Long type) {
COLLECTION.get().push(typeToCollection(type));
}
public String top() {
return COLLECTION.get().peek();
}
public void pop() {
COLLECTION.get().pop();
}
}
Now we can push collection name:
#Service
public class MongoAlertStorage implements IAlertStorage {
#Autowired
private CollectionHolder holder;
#Autowired
private AlertRepository alertRepository;
#Autowired
private Converters converters;
#Override
public Alert save(Alert alert) throws Exception {
try {
holder.push(alert);
return converters.of(alertRepository.save(converters.of(alert)));
} finally {
holder.pop();
}
}
}
It is time to specify dynamic collection in expression:
#Document(collection = "alert_#{#collection.top()}")
public class AlertPO implements Serializable {
// ...
}
Now, you can debug at org.springframework.expression.common.CompositeStringExpression#getValue(org.springframework.expression.EvaluationContext) to check the value evaluated, it should be what you want.

ID might not have been initialised with #GeneratedValue

I am making a RESTful API using Spring Boot and the spring data JPA. One of my model classes looks like so:
#Entity
#Data
#NoArgsConstructor(force = true, access = AccessLevel.PROTECTED)
public class Foo {
#Id
#GeneratedValue
private final long id;
private final Date date;
#ManyToOne
private Object bar;
public Foo(Object bar) {
this.bar = bar;
this.date = new Date();
}
}
However, it says that the id has not been initialised (I assume because it is not in my custom constructor) even though it is annotated with #GeneratedValue. How should I work around this?
Is there a better way of creating a POJO object?

Parent-child relation between two objects causes JSON StackOverflowError

I am trying to achieve a parent-child relation between some objects and I ran into a bit of trouble.
In my case, I am trying to store objects within other objects (e.g. container stores multiple items or other containers with items). The tricky part is that every object in the storage should be able to tell what it's outermost parent object is. While this seems to work in my in-memory database (using h2 at the moment), trying to get a JSON representation of all my storage items gives this (I return a List<StorageUnit> ):
Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: java.util.ArrayList[0]->com.warehousing.storage.FixedContentsCase["contents"]->java.util.ArrayList[0]->com.warehousing.storage.FixedContentsCase["contents"]->...
Here are the classes:
StorageUnit
#Entity
#Inheritance
public abstract class StorageUnit {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#ManyToOne
private Location location;
protected Long parentContainerId;
// <getters & setters>
public abstract List<StorageUnit> getContents();
}
FixedContentCase
#Entity
public class FixedContentsCase extends StorageUnit {
#OneToMany
private List<Item> items;
public FixedContentsCase() {
super();
items = new ArrayList<>();
}
// <getters & setters>
#Override
public List<StorageUnit> getContents() {
// Return the case itself and its contents
List<StorageUnit> contents = new ArrayList<>(Arrays.asList(this));
for (StorageUnit item : items)
contents.addAll(item.getContents());
return contents;
}
}
Item
#Entity
public class Item extends StorageUnit {
private String description;
public Item() {
super();
this.description = "";
}
// <getters & setters>
#Override
public List<StorageUnit> getContents() {
return Arrays.asList(this);
}
}
I have tried to annotate the StorageUnit class with #JsonIgnoreProperties("parentContainerId") but it didn't work. Annotating parentContainerId with #JsonIgnore didn't help either. I also tried annotating the getters instead of the attributes themselves (as per following). Is there a way to work around this or is some kind of design change necessary? Thanks!
Using Jackson this is definitely possible by annotations like #JsonIgnore or the DTO approach BugsForBreakfast mentioned.
I created a jackson MixIn handler to allow dynamic filtering which i use to avoid the boilerplate of DTOs
https://github.com/Antibrumm/jackson-antpathfilter
The examples in the readme should show how it works and if it‘s a possible solution for you.
Your problem is that you add the storage unit itself to its list of contents, leading to infinite recursion if you traverse the tree downwards. The solution: Use a reference and only serialize the object once, using #JsonIdentityInfo and #JsonIdentityReference:
public class MyTest {
#Test
public void myTest() throws JsonProcessingException {
final FixedContentsCase fcc = new FixedContentsCase();
fcc.setId(Long.valueOf(1));
final Item item = new Item();
item.setId(Long.valueOf(2));
item.setDescription("item 1");
fcc.getItems().add(item);
final ObjectMapper om = new ObjectMapper();
System.out.println(om.writeValueAsString(fcc));
}
}
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
#JsonIdentityReference(alwaysAsId = false)
class Item extends StorageUnit {
...
}
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
#JsonIdentityReference(alwaysAsId = false)
class FixedContentsCase extends StorageUnit {
...
}
abstract class StorageUnit {
...
}

Hibernate jpa does honour AttributeConveter

I have defined an attribute converter like this:
#Converter(autoApply = true)
public class MyConverter implements AttributeConverter<MyType, String> {
#Override
#Nullable
public String convertToDatabaseColumn(#Nullable final MyType attribute) {
LOG.log(Level.INFO, "Converting '{'{0}'}' to DB column", attribute);
return attribute != null ? map(attribute) : null;
}
#Override
#Nullable
public MyType convertToEntityAttribute(#Nullable final String dbData) {
LOG.log(Level.INFO, "Converting '{'{0}'}' to type", dbData);
return dbData != null ? map(dbData) : null;
}
}
Then in my entity class:
#Entity
public class MyEntity implements Serializable {
#Id
private Long id;
private MyType myData;
}
According to jpa2.1 specs, i do not have to annotate the attribute field with #Convert if i have specified autoApply on the converter.
Nonetheless, even if i do not specify the autoApply on the converter and specify the following:
#Entity
public class MyEntity implements Serializable {
#Id
private Long id;
#Convert(converter = MyConverter.class)
private MyType myData;
}
Hibernate still does not consider this converter.
What could i be doing wrong?
I have deleted the table and regenerated it, but nothing does help.
I have tried hibernate versions from 4.3.4 - 4.3.8 with no success, n wildfly 8.1
As aside note, My converter is declared in an entity-jar, which is then included in ejb-jar as a dependency.
Well.
After several hours, the solution seems to be simple.
My mistake was that i declared classes in the ejb-jars persistence.xml, instead of specifying the jar-file element. Therefore, the jpa hibernate annotation engine had no idea of my entity-jar, and could not scan it for the Converter annotation

Categories