Consider to dec lare/implement a mapping method: "java.lang.String map(java.lang.Object value)" during to use the Framework MapStruct - spring-boot

#Mapper(componentModel = "spring")
public interface MapperThings {
#MapMapping(keyTargetType = Object.class, valueTargetType = Object.class)
Map<String, String> toDto(Map<Object, Object> mapEntity);
List<Map <String, String>> toListDto(Collection<Map<Object, Object>> listEntity);
#MapMapping(keyTargetType = Object.class, valueTargetType = Object.class)
Map<Object, Object> toEntity(Map<String, String> mapDto);
List<Map<Object, Object> > toListEntity(Collection<Map<String, String>> listDto);
}
There is to generate without mistakes only :
#MapMapping(keyTargetType = Object.class, valueTargetType = Object.class)
Map<Object, Object> toEntity(Map<String, String> mapDto);
List<Map<Object, Object> > toListEntity(Collection<Map<String, String>> listDto);
I found temporary decision. But I would want to use the annotation #MapMapping.
#Mapper(componentModel = "spring")
public abstract class MapperMoviesAbstract {
public Map<String, String> toDto(Map<Object, Object> mapEntity) {
if(mapEntity == null) return null;
Map<String, String> stringMap = new HashMap<>();
for(Map.Entry<Object, Object> entry : mapEntity.entrySet()){
String key = (String) entry.getKey();
stringMap.put(key, mapEntity.get(key).toString());
}
return stringMap;
}
public abstract List< Map<String, String>> toListDto(Collection<Map<Object, Object>> listEntity);
}
According to the MapStruct documentation, using the #MapMapping annotation should generate a class that will perform the conversion.
But I get an error:
Can't map map key "java.lang.Object" to "java.lang.String ". Consider
to dec lare/implement a mapping method: "java.lang.String
map(java.lang.Object value)".
Do anyone have any ideas to do what?

The error message is telling you what to do. You need to provide a way to map Object into a String.
So you'll need a custom method like:
public String objectToString(Object object) {
// Your custom implementation
}

Related

Conditional IN from DynamoDb not working on java

I am working with DynamoDB with Spring Boot 2.1, and I'm facing an error when I need o user the clause IN during the conditional evaluation. Even with lines that fulfill the requirements, the query result is empty.
How can I return the lines from the table after explicit the result within the IN clause ?
public class DynamoRepository {
private final DynamoDBMapper dynamoDBMapper;
public Optional<List<USER>> query(String id) {
Map<String, String> ean = new HashMap<>();
ean.put("#status", "status");
Map<String, AttributeValue> eav = new HashMap<>();
eav.put(":id", new AttributeValue().withS(documento));
DynamoDBQueryExpression<USER> queryExpression = new DynamoDBQueryExpression<USER>()
.withKeyConditionExpression("id = :id")
.withFilterExpression("#status in (ACTIVE, PENDING)")
.withExpressionAttributeNames(ean)
.withExpressionAttributeValues(eav);
List<USER> query = dynamoDBMapper.query(USER.class, queryExpression);
return query.isEmpty() ? Optional.empty() : Optional.of(query);
}
}
After taking a while, my solution was to define the status' values as Expression Attribute Values like the code below
public class DynamoRepository {
private final DynamoDBMapper dynamoDBMapper;
public Optional<List<USER>> query(String id) {
Map<String, String> ean = new HashMap<>();
ean.put("#status", "status");
Map<String, AttributeValue> eav = new HashMap<>();
eav.put(":id", new AttributeValue().withS(documento));
eav.put(":active", new AttributeValue().withS("ACTIVE"));
eav.put(":pending", new AttributeValue().withS("PENDING"));
DynamoDBQueryExpression<USER> queryExpression = new DynamoDBQueryExpression<USER>()
.withKeyConditionExpression("id = :id")
.withFilterExpression("#status in (:active, :pending)")
.withExpressionAttributeNames(ean)
.withExpressionAttributeValues(eav);
List<USER> query = dynamoDBMapper.query(USER.class, queryExpression);
return query.isEmpty() ? Optional.empty() : Optional.of(query);
}
}

Using a Wrapper Type for a DTO in Spring + Jackson

I'm trying to find a more or less elegant way to handle PATCH http operations in Spring MVC.
Basically, I'd like to perform a "dual" Jackson deserialization of a JSON document from a Request Body: one to a Map, and the other to the target POJO. Ideally, I would like to perform this in a single PartialDto<T> instance, where T is my target DTO type.
Better giving an example. Let's say I currently have this PUT mapping in a REST Controller:
#PutMapping("/resource")
public MyDto updateWhole(#RequestBody MyDto dto) {
System.out.println("PUT: updating the whole object to " + dto);
return dto;
}
My idea is to build a PartialDto type that would provide both POJO representation of the request body, as well as the Map representation, like this:
#PatchMapping("/resource")
public MyDto updatePartial(#RequestBody PartialDto<MyDto> partial) {
System.out.println("PATCH: partial update of the object to " + partial);
final MyDto dto = partial.asDto();
// Do stuff using the deserialized POJO
final Map<String, Object> map = partial.asMap();
// Do stuff as a deserialized map...
return dto;
}
I hope this will allow me to further expand the PartialDto implementation so I can perform things like this:
if (partial.hasAttribute("myAttribute")) {
final String myAttribute = dto.getMyAttribute();
// ...
}
Or even using a metamodel generator:
if (partial.hasAttribute(MyDto_.myAttribute)) {
final String myAttribute = dto.getMyAttribute();
// ...
}
So the question is simple: Jackson can easily map a JSON document to a POJO. It can also easily map a JSON document to a java Map. How can I do both at the same time in a Wrapper object such as my PartialDto?
public class PartialDto<T> {
private final Map<String, Object> map;
private final T dto;
PartialDto(Map<String, Object> map, T dto) {
this.map = map;
this.dto = dto;
}
public T asDto() {
return this.dto;
}
public Map<String, Object> asMap() {
return Collections.unmodifiableMap(this.map);
}
}
I tried to use a GenericConverter like this (that, of course, I registered in Spring MVC's FormatterRegistry):
public class PartialDtoConverter implements GenericConverter {
private final ObjectMapper objectMapper;
public PartialDtoConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
#Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, PartialDto.class));
}
#Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
final Class<?> targetClazz = targetType.getResolvableType().getGeneric(0).getRawClass();
final Map<String, Object> map;
try {
map = objectMapper.readValue((String) source, Map.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e); // FIXME
}
final Object dto = objectMapper.convertValue(map, targetClazz);
return new PartialDto(map, dto) ;
}
}
And this converter works well when tested directly using Spring's ConversionService:
#SpringBootTest
class ConverterTest {
#Autowired
private ConversionService conversionService;
#Test
public void testPartialUpdate() throws Exception {
final MyDto dto = new MyDto()
.setIt("It");
final PartialDto<MyDto> partialDto = (PartialDto<MyDto>) conversionService.convert(
"{ \"it\": \"Plop\" }",
new TypeDescriptor(ResolvableType.forClass(String.class), null, null),
new TypeDescriptor(ResolvableType.forClassWithGenerics(PartialDto.class, MyDto.class), null, null)
);
Assertions.assertEquals("Plop", partialDto.asDto().getIt());
Assertions.assertEquals("Plop", partialDto.asMap().get("it"));
}
}
However, it doesn't seem to work in a #RequestBody such as shown above. Reminder:
#PatchMapping("/resource")
public MyDto updatePartial(#RequestBody PartialDto<MyDto> partial) {
// ...
}
Any idea is welcome.

SpringBoot map yaml to map of map

Is it possible to inject below yaml file to Spring Boot application as Map<String, Map<String, String> where tradeType will be the key of outer map and P and B will be the key value for inner map for.
tradeType:
P: B
S: S
securityCode:
ICI: ICICI Bank
BOB: Bank of Baroda
BOA: Bank of America
BOS: Bank of Singapore
As suggested this is how my class look.
#Configuration
#PropertySource("file:data/referenceDataMapping.yaml")
#ConfigurationProperties(prefix = "map")
public class ReferenceDataMapping {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private Map<String, Map<String, String>> entries;
#Override
public String toString() {
if (entries != null) {
logger.info(entries.toString());
return entries.toString();
} else {
return null;
}
}
public Map<String, Map<String, String>> getEntries() {
return entries;
}
public void setEntries(Map<String, Map<String, String>> entries) {
this.entries = entries;
}
}
from build.gradle
dependencies {
compile 'org.springframework.boot:spring-boot-starter-activemq:2.1.2.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-security:2.1.2.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-web:2.1.2.RELEASE'
compile 'org.apache.camel:camel-spring-boot-starter:2.23.1'
compile 'org.apache.camel:camel-quartz2:2.23.1'
compile 'org.apache.camel:camel-jms:2.23.1'
compile 'org.apache.camel:camel-jacksonxml:2.23.1'
compile 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
compile 'net.sf.saxon:Saxon-HE:9.9.1-1'
testCompile 'org.springframework.boot:spring-boot-starter-test:2.1.2.RELEASE'
testCompile 'org.springframework.security:spring-security-test:5.1.3.RELEASE'
}
referenceDataMapping.yaml
map:
entries:
tradeType:
P: B
S: S
securityCode:
ICI: ICICI Bank
BOB: Bank of Baroda
BOA: Bank of America
BOS: Bank of Singapore
Yes. It is possible.
#Configuration
#ConfigurationProperties(prefix="map")
public class Test {
private Map<String,Map<String,String>> entires;
public Map<String, Map<String, String>> getEntires() {
return entires;
}
public void setEntires(Map<String, Map<String, String>> entires) {
this.entires = entires;
}
}
application.yml:
map:
entires:
tradeType:
P: B
S: S
securityCode:
ICI: ICICI Bank
BOB: Bank of Baroda
BOA: Bank of America
BOS: Bank of Singapore
output:
{tradeType={P=B, S=S}, securityCode={ICI=ICICI Bank, BOB=Bank of Baroda, BOA=Bank of America, BOS=Bank of Singapore}}
Update:
As described in this github-issue. #PropertySource doesnt support yaml files. In that case, kindly follow this guide PropertySource-with-yaml-files
If the YAML is in different file than application.yml,
#Component
public class YourClass {
private Map<String, String> tradeType;
private Map<String, String> securityCode;
public Map<String, String> getTradeType() {
return tradeType;
}
public Map<String, String> getSecurityCode() {
return securityCode;
}
#PostConstruct
public void yamlFactory() {
YamlMapFactoryBean factory = new YamlMapFactoryBean();
factory.setResources(new ClassPathResource("your.yml"));
tradeType = (Map<String, String>) factory.getObject().get("tradeType");
securityCode = (Map<String, String>) factory.getObject().get("securityCode");
}
}

Cannot use Map as a JSON #RequestParam in Spring REST controller

This controller
#GetMapping("temp")
public String temp(#RequestParam(value = "foo") int foo,
#RequestParam(value = "bar") Map<String, String> bar) {
return "Hello";
}
Produces the following error:
{
"exception": "org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException",
"message": "Failed to convert value of type 'java.lang.String' to required type 'java.util.Map'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.util.Map': no matching editors or conversion strategy found"
}
What I want is to pass some JSON with bar parameter:
http://localhost:8089/temp?foo=7&bar=%7B%22a%22%3A%22b%22%7D, where foo is 7 and bar is {"a":"b"}
Why is Spring not able to do this simple conversion? Note that it works if the map is used as a #RequestBody of a POST request.
Here is the solution that worked:
Just define a custom converter from String to Map as a #Component. Then it will be registered automatically:
#Component
public class StringToMapConverter implements Converter<String, Map<String, String>> {
#Override
public Map<String, Object> convert(String source) {
try {
return new ObjectMapper().readValue(source, new TypeReference<Map<String, String>>() {});
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
}
}
If you want to use Map<String, String> you have to do the following:
#GetMapping("temp")
public String temp(#RequestParam Map<String, String> blah) {
System.out.println(blah.get("a"));
return "Hello";
}
And the URL for this is: http://localhost:8080/temp?a=b
With Map<String, String>you will have access to all your URL provided Request Params, so you can add ?c=d and access the value in your controller with blah.get("c");
For more information have a look at: http://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/spring-mvc-request-param/ at section Using Map with #RequestParam for multiple params
Update 1: If you want to pass a JSON as String you can try the following:
If you want to map the JSON you need to define a corresponding Java Object, so for your example try it with the entity:
public class YourObject {
private String a;
// getter, setter and NoArgsConstructor
}
Then make use of Jackson's ObjectMapper to map the JSON string to a Java entity:
#GetMapping("temp")
public String temp(#RequestParam Map<String, String> blah) {
YourObject yourObject =
new ObjectMapper().readValue(blah.get("bar"),
YourObject.class);
return "Hello";
}
For further information/different approaches have a look at: JSON parameter in spring MVC controller

Jersey REST service HashMap<String, ArrayList<String>> return

I want to return object of type HashMap> in GET method using XML or JSON. On client side I get keys of map, but values are null.
This is return class:
private HashMap<String, ArrayList<String>> hashMap = new HashMap<>();
public HashMap<String, ArrayList<String>> getHashMap() {
return hashMap;
}
public void setHashMap(HashMap<String, ArrayList<String>> hashMap) {
this.hashMap = hashMap;
}
This is GET method in service:
#GET
#Path("/mapa")
#Produces(MediaType.APPLICATION_XML)
public Test mapa() {
Test test = new Test();
HashMap<String, ArrayList<String>> hashMap = new HashMap<>();
ArrayList<String> list = new ArrayList<>();
list.add("first");
list.add("second");
hashMap.put("first", list);
test.setHashMap(hashMap);
return test;
}
I get this in browser:
<test>
<hashMap>
<entry>
<key>first</key>
<value/>
</entry>
</hashMap>
</test>
Why is value empty?
Have you tried to use a Response instead of Test Class ?
#GET
#Path("/mapa")
#Produces(MediaType.APPLICATION_XML)
public Response mapa() {
Test test = new Test();
HashMap<String, ArrayList<String>> hashMap = new HashMap<>();
ArrayList<String> list = new ArrayList<>();
list.add("first");
list.add("second");
hashMap.put("first", list);
test.setHashMap(hashMap);
return Response.status(200).entity(test).build();
}

Resources