Map parameter as GET param in Spring REST controller - spring

How I can pass a Map parameter as a GET param in url to Spring REST controller ?

It’s possible to bind all request parameters in a Map just by adding a Map object after the annotation:
#RequestMapping("/demo")
public String example(#RequestParam Map<String, String> map){
String apple = map.get("APPLE");//apple
String banana = map.get("BANANA");//banana
return apple + banana;
}
Request
/demo?APPLE=apple&BANANA=banana
Source -- https://reversecoding.net/spring-mvc-requestparam-binding-request-parameters/

There are different ways (but a simple #RequestParam('myMap')Map<String,String> does not work - maybe not true anymore!)
The (IMHO) easiest solution is to use a command object then you could use [key] in the url to specifiy the map key:
#Controller
#RequestMapping("/demo")
public class DemoController {
public static class Command{
private Map<String, String> myMap;
public Map<String, String> getMyMap() {return myMap;}
public void setMyMap(Map<String, String> myMap) {this.myMap = myMap;}
#Override
public String toString() {
return "Command [myMap=" + myMap + "]";
}
}
#RequestMapping(method=RequestMethod.GET)
public ModelAndView helloWorld(Command command) {
System.out.println(command);
return null;
}
}
Request: http://localhost:8080/demo?myMap[line1]=hello&myMap[line2]=world
Output: Command [myMap={line1=hello, line2=world}]
Tested with Spring Boot 1.2.7

Related

Creat a JSONObject : Spring boot

I would like to create a JSONObject :
#RequestMapping(value = "/test", method = RequestMethod.GET)
#ResponseBody
public JSONObject Test() {
JSONObject test = new JSONObject();
test.put("name","caroline");
return test;
}
it's giving me as a result :
{"map":{"name":"caroline"}}
But I was waiting for something like that :
{"name":"caroline"}
I don't know where is it the problem , I just followed this exemple
I tried with your code with a sample spring boot project and I get the error,
No converter for [class org.json.JSONObject]
The reason for this error is explained clearly here. To reiterate the answer, JSONObject classes don't have getters and hence the error. By default spring-boot starter web dependency has Jackson web support which can convert any POJO class to JSON object. So as the answer by #süleyman-can using a POJO is the right way to handle this.
In case, you can't use a POJO class because the fields in the response will be different for each request. For example, you have to send
{"a": "b"}
for one response and
{"c": "d"}
for another response, you can always use Map<String, String> like this,
#RequestMapping(value = "/test", method = RequestMethod.GET)
#ResponseBody
public Map<String, String> test() {
Map<String, String> test = new HashMap<>();
test.put("name","caroline");
return test;
}
and the response would come like this,
{"name":"caroline"}
I hope you are talking about org.json package
If you really want to use JSONObject to create your JSON, then the following code works. It's just that you can change the return type from JSONObject to String.
#RequestMapping(value = "/test", method = RequestMethod.GET)
#ResponseBody
public String Test() {
JSONObject test = new JSONObject();
test.put("name","caroline");
return test.toString();
}
you can try this
1- add this dependecy in pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.1</version>
</dependency>
2- i have this class for example
public class Car {
private String color;
private String type;
// standard getters setters
}
2- Java Object to JSON
ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car("yellow", "renault");
objectMapper.writeValue(new File("target/car.json"), car);
must result like it:
{"color":"yellow","type":"renault"}
3- JSON to Java Object
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
Car car = objectMapper.readValue(json, Car.class);
You can do by creating a Class
class MyResponseClass {
private String name;
public MyResponseClass(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Initial:
#RestController
class MyController {
#GetMapping("/test")
public MyResponseClass getMyResponseClass() {
final MyResponseClass test = new MyResponseClass("caroline");
return test;
}
}
I suggest you read this article: Building an Application with Spring Boot

Read multiple properties file in one go using Spring Boot?

I went through the link: How to pass a Map<String, String> with application.properties and other related links multiple times, but still its not working.
I'm using Spring Boot and Spring REST example. Link Question: How to by default execute the latest version of endpoint in Spring Boot REST?.
I've created mapping something like this and simply read the mapping
get.customers={GET: '/app-data/customers', VERSION: 'v1'}
post.customers={POST: '/app-data/customers', VERSION: 'v1'}
get.customers.custId={GET: '/app-data/customers/{custId}', VERSION: 'v2'}
Code:
private String resolveLastVersion() {
// read from configuration or something
return "2";
}
Code:
#Component
#ConfigurationProperties
#PropertySource("classpath:restendpoint.properties")
public class PriorityProcessor {
private final Map<String, String> priorityMap = new HashMap<>();
public Map<String, String> getPriority() {
return priorityMap;
}
}
Code:
I suggest the following implementation:
#ConfigurationProperties(prefix="request")
public class ConfigurationProps {
private List<Mapping> mapping;
public List<Mapping> getMapping() {
return mapping;
}
public void setMapping(List<Mapping> mapping) {
this.mapping = mapping;
}
}
Class Mapping will denote the information about the single mapping:
public class Mapping {
private String method;
private String url;
private String version;
public Mapping(String method, String url, String version) {
this.method = method;
this.url = url;
this.version = version;
}
public Mapping() {
}
// getters setters here
}
On the Configuration or spring boot application class (the one with main method):
#EnableConfigurationProperties(ConfigurationProps.class)
In the properties file put:
request.mapping[0].method=get
request.mapping[0].url=/customers
request.mapping[0].version=1
request.mapping[1].method=post
request.mapping[1].url=/students
request.mapping[1].version=2
In Filter (I assume you followed my suggestion from the linked question):
#Component
#Order(1)
public class LatestVersionFilter implements Filter {
private List<Mapping> mappings;
public LatestVersionFilter(ConfigurationProps props) {
this.mappings = props.getMapping();
}
}

JSON-B serializes Map keys using toString and not with registered Adapter

I have a JAX-RS service that returns a Map<Artifact, String> and I have registered a
public class ArtifactAdapter implements JsonbAdapter<Artifact, String>
which a see hit when deserializing the in-parameter but not when serializing the return value, instead the Artifact toString() is used. If I change the return type to a Artifact, the adapter is called. I was under the impression that the Map would be serialized with built-in ways and then the adapter would be called for the Artifact.
What would be the workaround? Register an Adapter for the whole Map?
I dumped the thread stack in my toString and it confirms my suspicions
at java.lang.Thread.dumpStack(Thread.java:1336)
Artifact.toString(Artifact.java:154)
at java.lang.String.valueOf(String.java:2994)
at org.eclipse.yasson.internal.serializer.MapSerializer.serializeInternal(MapSerializer.java:41)
at org.eclipse.yasson.internal.serializer.MapSerializer.serializeInternal(MapSerializer.java:30)
at org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serialize(AbstractContainerSerializer.java:63)
at org.eclipse.yasson.internal.Marshaller.serializeRoot(Marshaller.java:118)
at org.eclipse.yasson.internal.Marshaller.marshall(Marshaller.java:74)
at org.eclipse.yasson.internal.JsonBinding.toJson(JsonBinding.java:98)
is the serializer hell-bent on using toString at this point?
I tried
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class PersonAdapter implements JsonbAdapter{
#Override
public String adaptToJson(Person obj) throws Exception {
return obj.getName();
}
#Override
public Person adaptFromJson(String obj) throws Exception {
return new Person(obj);
}
}
public class Test {
public static void main(String[] args) {
Map<Person, Integer> data = new HashMap<>();
data.put(new Person("John"), 23);
JsonbConfig config = new JsonbConfig().withAdapters(new PersonAdapter());
Jsonb jsonb = JsonbBuilder.create(config);
System.out.println(jsonb.toJson(data, new HashMap<Person, Integer>() {
}.getClass().getGenericSuperclass()));
}
}
but still ended up with the toString() of Person
Thanks in advance,
Nik
https://github.com/eclipse-ee4j/yasson/issues/110 (in my case since that's the default provider for WildFly)

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

Get all documents from an index using spring-data-elasticsearch

I am trying to connect to my external ElasticSearch server with Spring Boot.
If I do a curl from command line, I get expected results.
curl "http://ipAddr:9200/indexName/TYPE/_search?pretty=true"
But getting this error when I try to access it via Spring Boot.
<html><body><h1>Whitelabel Error Page</h1><p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p><div id='created'>Mon Sep 11 12:39:15 IST 2017</div><div>There was an unexpected error (type=Internal Server Error, status=500).</div><div>Could not write JSON: (was java.lang.NullPointerException); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl["facets"])</div></body></html>
Not sure why a NullPointerException and what is aggregartion.impl
Here is my Spring Application:
Controller:
#RestController
public class PojoController {
#Autowired
PojoService pojoService;
#RequestMapping(value = "/", method=RequestMethod.GET)
public #ResponseBody String index() {
return new String("Welcome:)");
}
#RequestMapping(value = "/all", method = RequestMethod.GET,
produces = { MediaType.APPLICATION_JSON_VALUE })
#ResponseBody List<POJO> findAll() {
try {
List<POJO> pojoObj = pojoService.findAll();
return pojoObj;
} catch (Exception exp) {
exp.printStackTrace();
return null;
}
}
}
Repository:
#Repository
public interface PojoRepository extends ElasticsearchRepository<POJO, Integer> {
List<POJO> findAll();
}
Service:
#Service
public class POJOServiceImpl implements POJOService{
private POJORepository pojoRepository;
private ElasticsearchTemplate elasticsearchTemplate;
#Autowired
public void setPojoRepository(PojoRepository pojoRepository) {
this.pojoRepository = pojoRepository;
}
public POJO findOne(String id) {
return pojoRepository.findOne(id);
}
public List<POJO> findAll() {
return (List<POJO>) pojoRepository.findAll();
}
}
POJO class:
#Document(indexName = "INDEX", type = "TYPE")
public class POJO {
#Id
private Integer id;
private String name;
public POJO(){
// empty
}
public POJO(Integerid, String name) {
super();
this.id = id;
this.name = name;
}
// getters and setters
}
I should be able to query all the documents in the index. Later on, I will try and use filters etc.
Any help is appreciated. Thanks :)
It looks like Jackson has a problem with handling your POJO (probably related to this issue: DATAES-274) - the problematic part is casting in repository from Iterable collection to List.
Update
In case of repositories, spring-data-elasticsearch behaves a bit different than you would expect. Taking your example:
#Repository
public interface PojoRepository extends ElasticsearchRepository<POJO, Integer> {
List<POJO> findAll();
}
and after calling in your rest controller:
List<POJO> pojoObj = pojoService.findAll();
in debugger you will see something like this:
You would expect that pojoObj list contains objects of POJO class.
And here comes the surprise - pojoObj ArrayList contains one object of AggregatedPageImpl type and its content field is the right list that contains your POJO objects.
This is the reason why you get:
Could not write JSON: ... java.util.ArrayList[0]->org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl[\"facets\"])
As I wrote before, Jackson cannot handle this while serializing POJO objects.
Solution 1
Let repositories return Iterable collection (by default).
#Repository
public interface PojoRepository extends ElasticsearchRepository<POJO, Integer> {
}
Move the conversion part to the service but use some utility method (here with Guava) in order to have it like this:
import com.google.common.collect.Lists;
public List<POJO> findAll() {
return Lists.newArrayList(pojoRepository.findAll());
}
Solution 2
Use Page in repository (here simplified version without parameters):
#Repository
public interface PojoRepository extends ElasticsearchRepository<POJO, Integer> {
Page<TestDto> findAll();
}
If you still want to operate on list - get content from page in service:
public List<POJO> findAll() {
return testDtoRepository.findAll().getContent();
}

Resources