Map Only Particular Fields from DTO -> Model Using Map-Struct - java

I have use case where i need to map or fill data for particular fields
for example : I Have a user Model which i need to convert to UserDTO with only
particular fields like username and accountId.
MODEL :
public class UserCore{
private String accountId;
private String username;
private String workEmail;
private String firstName;
private String password;
private String hashedPassword;
}
UserDTO :
public class UserCoreDTO{
private String accountId;
private String username;
private String workEmail;
private String firstName;
private String password;
private String hashedPassword;
}
is there any way in map-struct so that i can map only particular fields from source to destination
for example :
UserMapper mapper = Mappers.getMapper( UserMapper.class );
mapper.map(fieldsToFetch,source,destination);

Here's an example form the docs:
#Mapper
public interface FishTankMapper {
#Mappings({
#Mapping(target = "fish.kind", source = "fish.type"),
#Mapping(target = "fish.name", ignore = true),
#Mapping(target = "ornament", source = "interior.ornament"),
#Mapping(target = "material.materialType", source = "material"),
#Mapping(target = "quality.report.organisation.name", source = "quality.report.organisationName")
})
FishTankDto map( FishTank source );
}
ignore = true will probably work for all fields, not just nested fields as in the example.

Related

Update User Detail api returns DataIntegrityViolationException

I'm creating an update API that updates the profile of the super admin, I mapped the member table to a DTO, on the member table password is set to not null and I did not include the password field on the dto because there's a provision for that be, when I tested the API on postman it returned on the console
DataIntegrityViolationException
SQL Error: 1048, SQLState: 23000
Column 'password' cannot be null
Here is my code
Dto
#Getter
#Setter
public class UpdateProfileDto {
#NotNull(message = "{member.firstName.notNull}")
#JsonProperty("first_name")
private String firstName;
#NotNull(message = "{member.lastName.notNull}")
#JsonProperty("last_name")
private String lastName;
#JsonProperty("nationality")
private Long nationality;
#JsonProperty("country_of_residence")
private Long countryOfResidence;
#JsonProperty("date_of_birth")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
#JsonFormat(pattern = "dd-MM-yyyy")
#Past(message = "{customer.dateOfBirth.past}")
private Date dateOfBirth;
#JsonProperty("current_job_title")
private String currentJobTitle;
#NotNull(message = "{member.emailAddress.notNull}")
#JsonProperty("email_address")
private String emailAddress;
#JsonProperty("username")
private String username;
#NotNull(message = "{member.phoneNumber.notNull}")
#PhoneNumber
#JsonProperty("phone_number")
private String phoneNumber;
#Size(max = 300, message = "{member.city.size}")
#JsonProperty("city")
private String city;
#Size(max = 300, message = "{member.state.size}")
#JsonProperty("state")
private String state;
}
ServiceImpl
#Override
#Transactional
public Member updateProfile(UpdateProfileDto body) {
Member superAdmin = repository.getOne(id);
if (superAdmin == null) {
throw new MemberNotFoundException(id);
}
Optional<Role> existingRole = roleJpaRepository.findByCode(RoleType.SUPER_ADMINISTRATOR.getValue());
if (existingRole.isEmpty()) {
throw new RoleNotFoundException(RoleType.SUPER_ADMINISTRATOR.getValue());
}
Member existing;
existing = mapper.map(body, Member.class);
existing.setPassword(superAdmin.getPassword());
existing.getRoles().add(existingRole.get());
existing.setNationality(countryRepository.getOne(body.getNationality()));
existing.setCountryOfResidence(countryRepository.getOne(body.getCountryOfResidence()));
return adminJpaRepository.save(existing);
}
Controller
#RestController
#RequestMapping(
value = "super-admin",
produces = { MediaType.APPLICATION_JSON_VALUE }
)
public class SuperAdminController {
private final SuperAdminService service;
public SuperAdminController(SuperAdminService service) {
this.service = service;
}
#PutMapping("/update")
public Member updateProfile(#Valid #RequestBody UpdateProfileDto body){
Member superAdmin = service.updateProfile(body);
return superAdmin;
}
}
The password bug has been fixed(changes reflected in serviceImpl), but when I run the code it returned Duplicate entry 'ijava#gmail.com-111803918380' for key 'member.email_address_phone_number_uq' email, and the phone number is set as a unique constraint in the member table, how can I bypass this?
You have few options, depending on your exact use case.
Extract existing password, using unique property in UpdateProfileDto, email looks like it can do the job.
Pseudocode:
Member existing = repository.findByEmail;
Member superAdmin = mapper.map(body, Member.class);
superAdmin.setPassword(existing.getPassword());
Set a dummy value for password, to be updated later on.
superAdmin.setPassword("dummy-password");
Make the column nullable in database.

Map nested beans list : "org.mapstruct.Mapping"

I want to map the Abc class to AbcDTO using "org.mapstruct.Mapping"
class Abc {
private List<Xyz> xyz = null;
private String uvw;
private String cde;
}
class AbcDTO{
private List<XyzDTO> xyz = null;
private String uvw;
private String cde;
}
class Xyz{
private String type;
private String value;
private String docId;
}
class XyzDTO{
private String type;
private String value;
private DocDTO document;
}
I tried to map the classes by using the annotation:
#Mappings({
#Mapping(source = "xyz.docId", target = "xyz.doc")
})
abcDTO abcToabcDTO(abc abc)
Can someone please help with how do i iterate through the nested beans and map the docId to doc?
If the names are same they map automatically but I want to map from docId to doc.
when you want map list you can define it :
#Mapping(source="docId", target="doc")
XyzDTO xyzToXyzDTO(XyZ xyz);
#Mapping(source="xyz", target="xyz") //useless if two lists got same name, but good for comprehention
AbcDTO abcToAbcDTA(Abc abc);
It should be as below (
you can give it a try):
#Mappings({
#Mapping(target="doc", source="abc.docId")
})
AbcDTO abcToabcDTO(Abc abc);```

Jackson: deserialize with Builder along with standard setters/getters?

Is it possible with Jackson to deserialize json with Builder pattern as well as with default setter and getter approach?
My object is created with Builder that covers only required (final) fields, but I have non-final fields with some values as well that need to be deserialized with setters.
Here is the sample that throws an exception in an attempt to deserialize it with:
new ObjectMapper().readValue(json, Foo.class);
json - json representation serialized with default Jackson serializer, like:
objectMapper.writeValueAsString(foo);
class
#Getter
#Setter
#ToString
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonDeserialize(builder = Foo.Builder.class)
public class Foo {
private final String key;
private final Long user;
private final String action;
private final String material;
private final String currency;
private Foo(String key, Long user, String action, String material, String currency) {
this.key = key;
this.user = user;
this.action = action;
this.material = material;
this.currency = currency;
}
public static class Builder {
private String key;
private Long user;
private String action;
private String material;
private String currency;
#JsonProperty("key")
public Foo.Builder withKey(String key) {
this.key = key;
return this;
}
#JsonProperty("user")
public Foo.Builder withUser(Long user) {
this.user = user;
return this;
}
#JsonProperty("action")
public Foo.Builder withAction(String action) {
this.action = action;
return this;
}
/// other 'with' setters....
}
#JsonProperty("state")
private int state;
#JsonProperty("stat")
private String stat;
#JsonProperty("step")
private String step;
}
The exception it throws like :
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "state" (class com.Foo$Builder), not marked as
ignorable (5 known properties: "key", "user", "action", "material",
"currency",])
If not possible what workaround is the cheapest?
Two things that are suspicious:
You are willing to use the builder inside the Foo class. In that case you should correct the specification
(SessionData.Builder.class is not correct in that case).
You are indeed trying to use an external builder. In this case you should remove or at least mark as ignorable the inner builder, this seems to be the reason of the excetpion you are getting.
In both cases you should make sure the final method to get the Foo instance is called build() otherwise you should annotate the builder with a #JsonPOJOBuilder(buildMethodName = "nameOfMethod", withPrefix = "set").

Java object not populated from json request for inner class

Have searched in different sites but couldn't find correct answer, hence posting this request though it could possible duplicates.sorry for that.
I am sending the below json request to my back-end service and converting to java object for processing. I can see the request body passed to my service but when i convert from json to java object , values are not populating
{
"data":{
"username":"martin",
"customerId":1234567890,
"firstName":"john",
"lastName":"smith",
"password":"p#ssrr0rd##12",
"email":"john.smith#gmail.com",
"contactNumber":"0342323443",
"department":"sports",
"location":"texas",
"status":"unlocked",
"OrderConfigs":[
{
"vpnId":"N4234554R",
"serviceId":"connectNow",
"serviceType":"WRLIP",
"ipAddress":"10.101.10.3",
"fRoute":[
"10.255.253.0/30",
" 10.255.254.0/30"
],
"timeout":1800,
"mapId":"test_map"
}
]
}
}
My Parser class have something like,
JSONObject requestJSON = new JSONObject(requestBody).getJSONObject("data");
ObjectMapper mapper = new ObjectMapper();
final String jsonData = requestJSON.toString();
OrderDTO mappedObject= mapper.readValue(jsonData , OrderDTO .class);
// I can see value coming from front-end but not populating in the mappedObject
My OrderDTO.java
#JsonInclude(value = Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true,value = {"hibernateLazyInitializer", "handler", "created"})
public class OrderDTO {
private String username;
private long customerId;
private String source;
private String firstName;
private String lastName;
private String email;
private String contactNumber;
private String password;
private String department;
private String location;
private String status;
private List<OrderConfig> OrderConfigs;
#JsonInclude(value = Include.NON_NULL)
public class OrderConfig {
private String vpnId;
private String serviceId;
private String serviceType;
private String ipAddress;
private String mapId;
private String[] fRoutes;
private Map<String, Object> attributes;
private SubConfig subConfig;
private String routeFlag;
getter/setters
.....
}
all setter/getter
}
Not sure what I'm missing here. Is this right way to do?
If your are trying to use inner class, correct way to use is to declare it static for Jackson to work with inner classes.
For reference check this
code changes made are
#JsonInclude(value = Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
static class OrderConfig {
Make sure that your json tag names match with variable names of java object
Ex : "fRoute":[
"10.255.253.0/30",
" 10.255.254.0/30"
],
private String[] fRoutes;
OrderConfigs fields will not be initialized, just modify your bean as
#JsonProperty("OrderConfigs")
private List<OrderConfig> orderConfigs;
// setter and getter as setOrderConfigs / getOrderConfigs
See my answer here. (same issue)

Spring HATEOAS with nested resources and JsonView filtering

I am trying to add HATEOAS links with Resource<>, while also filtering with #JsonView. However, I don't know how to add the links to nested objects.
In the project on on Github, I've expanded on this project (adding in the open pull request to make it work without nested resources), adding the "Character" entity which has a nested User.
When accessing the ~/characters/resource-filtered route, it is expected that the nested User "player" appear with the firstNm and bioDetails fields, and with Spring generated links to itself, but without the userId and lastNm fields.
I have the filtering working correctly, but I cannot find an example of nested resources which fits with the ResourceAssembler paradigm. It appears to be necessary to use a ResourceAssembler to make #JsonView work.
Any help reconciling these two concepts would be appreciated. If you can crack it entirely, consider sending me a pull request.
User.java
//package and imports
...
public class User implements Serializable {
#JsonView(UserView.Detail.class)
private Long userId;
#JsonView({ UserView.Summary.class, CharacterView.Summary.class })
private String bioDetails;
#JsonView({ UserView.Summary.class, CharacterView.Summary.class })
private String firstNm;
#JsonView({ UserView.Detail.class, CharacterView.Detail.class })
private String lastNm;
public User(Long userId, String firstNm, String lastNm) {
this.userId = userId;
this.firstNm = firstNm;
this.lastNm = lastNm;
}
public User(Long userId) {
this.userId = userId;
}
...
// getters and setters
...
}
CharacterModel.java
//package and imports
...
#Entity
public class CharacterModel implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(CharacterView.Summary.class)
private Long characterId;
#JsonView(CharacterView.Detail.class)
private String biography;
#JsonView(CharacterView.Summary.class)
private String name;
#JsonView(CharacterView.Summary.class)
private User player;
public CharacterModel(Long characterId, String name, String biography, User player) {
this.characterId = characterId;
this.name = name;
this.biography = biography;
this.player = player;
}
public CharacterModel(Long characterId) {
this.characterId = characterId;
}
...
// getters and setters
...
}
CharacterController.java
//package and imports
...
#RestController
#RequestMapping("/characters")
public class CharacterController {
#Autowired
private CharacterResourceAssembler characterResourceAssembler;
...
#JsonView(CharacterView.Summary.class)
#RequestMapping(value = "/resource-filtered", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public Resource<CharacterModel> getFilteredCharacterWithResource() {
CharacterModel model = new CharacterModel(1L, "TEST NAME", "TEST BIOGRAPHY", new User(1L, "Fred", "Flintstone"));
return characterResourceAssembler.toResource(model);
}
...
}
CharacterResourceAssembler.java
//package and imports
...
#Component
public class CharacterResourceAssembler implements ResourceAssembler<CharacterModel, Resource<CharacterModel>>{
#Override
public Resource<CharacterModel> toResource(CharacterModel user) {
Resource<CharacterModel> resource = new Resource<CharacterModel>(user);
resource.add(linkTo(CharacterController.class).withSelfRel());
return resource;
}
}

Categories