I have class:
public class User implements Body {
private Integer userId;
private String userName;
private String emailId;
//getter and setter
}
I want with Jackson mapper exclude Body class because I get error
ObjectMapper mapper = new ObjectMapper();
User user = new User;
String jsonString = mapper.writeValueAsString(user);
How I can convert object to JSON with exclude all extended or implementation class? I need convert only User class without Body
My super class have many public method like this:
public final Enumeration method(String email) {
throw new RuntimeException("Error");
}
public final Object method(String name) {
throw new RuntimeException("Error");
}
Simply annotate the class you are serializing and list the properties to exclude.
#JsonIgnoreProperties({"bodyPropertyA", "bodyPropertyB", "bodyPropertyC"})
public class User implements Body {
private Integer userId;
private String userName;
private String emailId;
}
Try to add a filter to exclude parent fields. Here is a test:
public class JacksonTest {
#Test
public void excludeParent() throws JsonProcessingException {
ObjectMapper objectMapper= new ObjectMapper();
SimpleBeanPropertyFilter theFilter = SimpleBeanPropertyFilter
.serializeAllExcept("name","phone");
FilterProvider filters = new SimpleFilterProvider()
.addFilter("filterPerson", theFilter);
objectMapper.setFilterProvider(filters);
final User user = new User("email1",12);
user.setName("personName");
user.setPhone("12345");
System.out.println(objectMapper.writeValueAsString(user));
}
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#JsonFilter("filterPerson")
class User extends Person{
String email;
int age;
}
#Data
#AllArgsConstructor
#NoArgsConstructor
class Person{
String name;
String phone;
}
console:
{"email":"email1","age":12}
Try this:
#JsonIgnore
public interface HiddenBody extends Body {
}
public class User implements HiddenBody {
//
}
The Liskov Substitution Principle says everything should still compile and execute OK.
Related
I have a class:
public class User extends Body {
private Integer userId;
private String userName;
private String emailId;
//getters and setters
}
I want to exclude Body class properties with Jackson mapper because I get an error.
ObjectMapper mapper = new ObjectMapper();
User user = new User;
String jsonString = mapper.writeValueAsString(user);
How I can convert object to JSON with excluding all extended or implementation class? I need convert only User class without Body
My super class has many public method like this:
public final Enumeration method(String email) {
throw new RuntimeException("Error");
}
public final Object method(String name) {
throw new RuntimeException("Error");
}
1. Using #JsonView annotation
Jackson library has #JsonView annotation which allows to provide different views of the serialized class.
You need to create a class describing different views like this:
public class Views {
public interface Base {} // view of Base class properties
public interface Child {} // view of Child class properties (i.e. User)
}
Then you mark up the fields/getters in the base Body class with #JsonView(Views.Base.class):
public class Body {
#JsonView(Views.Base.class)
private int foo;
#JsonView(Views.Base.class)
public String getBar() {
return "bar";
}
// other getters/setters
}
The User class can be marked at class level:
#JsonView(Views.Child.class)
public class User extends Body {
private Integer userId;
private String userName;
private String email;
// getters/setters
}
And when serializing with ObjectMapper you set its writer up to use specific view writerWithView:
ObjectMapper mapper = new ObjectMapper();//
User user = new User(1, "Jack", "jack#company.com");
String json = mapper.writerWithView(Views.Child.class).writeValueAsString(user);
System.out.println("custom view: " + json);
System.out.println("full view: " + mapper.writeValueAsString(user));
Output:
custom view: {"userId":1,"name":"Jack","email":"jack#company.com"}
full view: {"foo":0,"userId":1,"name":"Jack","email":"jack#company.com","bar":"bar"}
2. Using #JsonIgnoreProperties annotation
It is also possible to customize the view of the child class by ignoring its parent class' properties:
#JsonIgnoreProperties({"foo", "bar"})
public class User extends Body {
private Integer userId;
private String name;
private String email;
}
Then there's no need to configure the writer ObjectMapper instance:
System.out.println("base class fields ignored: " + mapper.writeValueAsString(user));
Output:
base class fields ignored: {"userId":1,"name":"Jack","email":"jack#company.com"}
3. Configure ObjectMapper to set custom JacksonAnnotationIntrospector
It is also possible to configure the ObjectMapper instance to set a custom annotation introspector to completely ignore properties belonging to the parent Body class:
// imports for Jackson v.2.x
// import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
// import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
// imports for Jackson v.1.9
import org.codehaus.jackson.map.introspect.AnnotatedMember;
import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector;
class IgnoreBodyClassIntrospector extends JacksonAnnotationIntrospector {
#Override
public boolean hasIgnoreMarker(final AnnotatedMember member) {
return member.getDeclaringClass() == Body.class || super.hasIgnoreMarker(member);
}
}
Configure ObjectMapper and serialize User without any code changes to Body and User:
ObjectMapper mapper = new ObjectMapper()
.setAnnotationIntrospector(new IgnoreBodyClassIntrospector());
User user = new User(3, "Nobody", "nobody#company.com");
System.out.println("no base class fields: " + mapper.writeValueAsString(user));
Output:
no base class fields: {"userId":3,"name":"Nobody","email":"nobody#company.com"}
Gotta question regarding mapStruct. I have case where I extend class from base entity and not sure how to map it. Here is my case.
BaseEntity:
public class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id")
private Long id;
}
BaseDto:
public class BaseDto {
private Long id;
}
UserEntity:
public class User extends BaseEntity {
private String name;
private String lastName;
private String username;
private String password;
private String profilePicturePath;
}
UserDto:
public class UserDto extends BaseDto {
private String name;
private String lastName;
private String username;
private String password;
private String profilePicturePath;
}
And mapper is like this:
#Mapper(uses = {BaseMapper.class})
public interface UserMapper {
User userDtoToUser(UserDto userDto);
UserDto userToUserDto(User user);
}
BaseMapper:
#Mapper
public interface BaseMapper {
BaseEntity dtoToEntity(BaseDto baseDto);
BaseDto entityToDto(BaseEntity baseEntity);
}
Problem is that I don't get ID property mapped.
Thank you for your time.
EDIT:
There is no error shown, in mapper implementation (generated code) there is no mapping for that ID:
#Override
public User userDtoToUser(UserDto userDto) {
if ( userDto == null ) {
return null;
}
UserBuilder user = User.builder();
user.name( userDto.getName() );
user.lastName( userDto.getLastName() );
user.username( userDto.getUsername() );
user.password( userDto.getPassword() );
user.profilePicturePath( userDto.getProfilePicturePath() );
return user.build();
}
I'm guessing (as you have not put buider code) the problem is that your builder class does not include parent class field. MapStruct makes some assumption while generating code for mapper. From documentation -
The default implementation of the BuilderProvider assumes the
following:
The type has a parameterless public static builder creation method
that returns a builder. So for example Person has a public static
method that returns PersonBuilder.
The builder type has a parameterless public method (build method)
that returns the type being build In our example PersonBuilder has a
method returning Person.
In case there are multiple build methods, MapStruct will look for a
method called build, if such method exists then this would be used,
otherwise a compilation error would be created.
If you are using Lombok, you can solve this by using #SuperBuilder as -
#SuperBuilder
#Getter
#ToString
public class UserDto extends BaseDto {
private String name;
private String lastName;
private String username;
private String password;
private String profilePicturePath;
}
#Getter
#SuperBuilder
class BaseDto {
private Long id;
}
#SuperBuilder
#Getter
#ToString
public class User extends BaseEntity {
private String name;
private String lastName;
private String username;
private String password;
private String profilePicturePath;
}
#Setter
#Getter
#SuperBuilder
class BaseEntity {
private Long id;
}
And generated could looks like -
#Override
public User userDtoToUser(UserDto userDto) {
if ( userDto == null ) {
return null;
}
UserBuilder<?, ?> user = User.builder();
user.id( userDto.getId() );
user.name( userDto.getName() );
user.lastName( userDto.getLastName() );
user.username( userDto.getUsername() );
user.password( userDto.getPassword() );
user.profilePicturePath( userDto.getProfilePicturePath() );
return user.build();
}
I have a Entity class something like this:
#Entity
public class Website {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String url;
public Website() {
//Constructor
//getters and setters
}
here is the DTO class:
public class WebsiteDto {
private Integer id;
private String name;
private String url;
public WebsiteVo() {
//Constructor
//getters and setters
}
I have the WebsiteMapper something like this:
#Component
public class WebsiteMapper {
public List<WebsiteDto> getWebsiteList() {
return repository.findAll().stream().map(w -> {
WebsiteDto dto = new WebsiteVo(w.getId(), w.getName(), w.getUrl());
return dto;
}).collect(Collectors.toList());
I also have Repository Interface:
public interface WebsiteRepository extends JpaRepository<Website, Integer> {
}
I want now to convert DTO to entity using my class WebsiteMapper. Because I did the conversion in this class. How I can do it?
How about using BeanUtils provided by spring org.springframework.beans.BeanUtils, something like this
public List<WebsiteDto> getWebsiteList() {
return repository.findAll().stream().map(w -> {
WebsiteDto dto = new WebsiteVo();
BeanUtils.copyProperties(w, dto); // copys all variables with same name and type
return dto;
})
.collect(Collectors.toList());
}
Hi I guess you wish to converting your entity to DTO. It's quite simple. Create static methods in your DTO class or any util class. The return type should be your DTO type.
e.g.
public class WebsiteDto {
private Integer id;
private String name;
private String url;
public static WebsiteDto export(Website website) {
// Return a new instance of your website DTO
return new WebsiteDto(
website.getId(),
website.getName(),
website.getUrl()
);
}
public static List<WebsiteDto> export(List<Website> websites) {
// Return a new instance of your website DTO list
return websites.stream().map(website -> {
return new WebsiteDto(
website.getName(),
website.getUrl()
}).collect(Collectors.toList());
}
}
NOTE You can also convert your DTO to entity using similar method.
I'm developing a website for my company, and I use Spring as my backend.
There is a situation now, where I need to use one of my Utils method twice, but for different DAOs.
In order to avoid code duplication, I was wondering how can I use Java Generics in order to make this method usable for both cases. The method just count one of the fields which is common for both DAOs.
Util method :
SeverityCount calculateSeveritiesCount(List<?> events){
if(null == events){
return new SeverityCount();
}
if(events.get(1) instanceof EventDAO){
events = (List<EventDAO>)events;
}
else if (events.get(1) instanceof EventsByAreaDAO) {
events = (List<EventsByAreaDAO>)events;
}
Map<String, Long> severityCountMap = events.stream().collect(
Collectors.groupingBy(
EventDAO::getSeverity, //It should be EventDAO or EventsByAreaDAO. both has severity field.
Collectors.counting())
);
return mapper.convertValue(severityCountMap, SeverityCount.class);
}
Event DAO:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "events")
public class EventDAO {
#Id #Column(name = "uid")
private String uID;
private String date;
private String severity;
}
Area DAO:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "events")
public class EventsByRegionDAO {
#Id #Column(name = "uid")
private String uID;
private String date;
private String type;
private String severity;
private String area;
private String server;
}
This is how I call it from the service:
SeverityCount severitiesCount = Utils.calculateSeveritiesCount(eventsList); //EventsList could be list of EventDAO or EventsByAreaDAO
You can change the method to
SeverityCount calculateSeveritiesCount(List<? extends SeverityCalculable> events)
where SeverityCalculable
interface SeverityCalculable {
String getSeverity(); // implemente getter in all subclasses
}
Have all your relevant classes implement this interface.
public class EventDAO implements SeverityCalculable {
// ...
#Override
public String getSeverity() {
return this.severity;
}
}
Now in your method, remove the casts and it should become something like this:
SeverityCount calculateSeveritiesCount(List<? extends SeverityCalculable> events) {
if(null == events){
return new SeverityCount();
}
Map<String, Long> severityCountMap = events.stream().collect(
Collectors.groupingBy(
SeverityCalculable::getSeverity,
Collectors.counting()
)
);
return mapper.convertValue(severityCountMap, SeverityCount.class);
}
Since both DAO's have a severity property, they could potentially implement a common interface, say SeverityAware:
public interface SeverityAware {
public String getSeverity();
}
public class EventsByRegionDAO implements SeverityAware { .. }
public class EventDAO implements SeverityAware { .. }
Further, your method could now accept subtypes of this interface:
SeverityCount calculateSeveritiesCount(List<? extends SeverityAware> events){
if (null == events){
return new SeverityCount();
}
Map<String, Long> severityCountMap = events.stream().collect(Collectors.groupingBy(
SeverityAware::getSeverity,Collectors.counting())
);
return mapper.convertValue(severityCountMap, SeverityCount.class);
}
I'm trying to deserialize JSON Array, which is persisted into my MongoDB, to a Java object by using Jackson. I found many tutorials mentioned to handle this polymorphism by adding:
#JsonTypeInfo(use=Id.CLASS,property="_class")
to a Super-class. However, in my case, I can't be able to modify the Super-class. So, are there some solutions to solve it without modifying the Super-class? Here is my code:
public class User {
#JsonProperty("_id")
private String id;
private List<Identity> identities; // <-- My List contains objects of an abstract class; Identity
public User(){
identities = new ArrayList<Identity>();
}
public static Iterable<User> findAllUsers(){
return users().find().as(User.class); // Always give me the errors
}
/*More code*/
}
It always give me the error - Can not construct instance of securesocial.core.Identity, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information.
You can use #JsonDeserilize annotation to bind a concrete implementation class to an abstract class. If you cannot modify your abstract class you can use the Jackson Mix-in annotations to tell Jackson how to find the implementation class.
Here is an example:
public class JacksonAbstract {
public static class User {
private final String id;
private final List<Identity> identities;
#JsonCreator
public User(#JsonProperty("_id") String id, #JsonProperty("identities") List<Identity> identities) {
this.id = id;
this.identities = identities;
}
#JsonProperty("_id")
public String getId() {
return id;
}
public List<Identity> getIdentities() {
return identities;
}
}
public static abstract class Identity {
public abstract String getField();
}
#JsonDeserialize(as = IdentityImpl.class)
public static abstract class IdentityMixIn {
}
public static class IdentityImpl extends Identity {
private final String field;
public IdentityImpl(#JsonProperty("field") String field) {
this.field = field;
}
#Override
public String getField() {
return field;
}
}
public static void main(String[] args) throws IOException {
User u = new User("myId", Collections.<Identity>singletonList(new IdentityImpl("myField")));
ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Identity.class, IdentityMixIn.class);
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(u);
System.out.println(json);
System.out.println(mapper.readValue(json, User.class));
}
}