I have a webservice which returns a "Map", I am trying to read this object from the Response(javax.ws.rs.core).
something like this:
ex : Map<String, Object> temp = response.readEntity(Map.class)
but this doesn't seem to work.
My question is how do I read a Map entity from a response object?
Found a way to read the Map entity from response.I guess I needed to provide the implementation class for Map.
response.readEntity(new GenericType<HashMap<String, Object>>() { });
Related
How to pass a list of integers as json data to a Spring Boot rest API without creating a custom intermediate object? I know you can create a DTO sort of object to map the request and I've done that successfully but is there a way to receive it as a collection/array of IDs without having to create a class just for this purpose? I tried to do it without any luck. Also tried to map it to a collection of the Entity the IDs refer to also without any luck. A way to do this, or alternatives that are more idiomatic to Spring Boot would be welcomed.
Sample of request body that's being sent to the endpoint:
{
"products": [
"1",
"2"
]
}
It seems answer of fps should be suitable, but here one caveat
Even if your class is annotated as #RestController, you need to use #RequestBody when using Map as input (with POJO this annotation can be avoided)
Bellow code works fine:
#PostMapping("/products")
#ResponseStatus(value = HttpStatus.OK)
public Object createProducts(#RequestBody Map<String, List<String>> input) {
return input;
}
More agile way is using Jackson classes.
It allows to use not only Strings, but all "native" json structures.
You can check JsonNode.isNumber(), JsonNode.isTextual(), JsonNode.isArray(), JsonNode.isObject() and so on.
#PostMapping("/products")
#ResponseStatus(value = HttpStatus.OK)
public Object createProducts(#RequestBody Map<String, JsonNode> input) {
return input;
}
Using this code
Map<String,Object> payloadMap = new HashMap<String,Object>();
payloadMap = (Map<String,Object>) new Gson().fromJson(result, payloadMap.getClass());
, I convert this json:
{
"name":"name1",
"job":"prosecutor",
"department": {
"department_name":"prosecutor's office"
}
}
to the map (map with unlimited number of child maps):
This done well, but now I want to get an access to values of child (nested) maps.
In parent map child maps "wrapped" to Object.
So, I tried to get "wrapped" child maps from the Object-values of parent map.
public void mapRequestNode (Map<String,Object> payloadMap) {
payloadMap.entrySet().forEach(node->this.getDataFromNode(node));
}
As you can see from the above picture, there are no way to use child map "department", which had been "wrapped" to Object. I have an access to Object-methods, but not to the Map-methods (for example, I cant use "value.get("department_name")". I tried cast "(Map<String, Object>)value", but without success...
The "department" name in case above is only for example! I dont know concrete name of json child-objects. There may be unlimited number of names! So I cant use something like this "payloadMap.get("department")"
Following
((Map<String, Object>)payloadMap.get("department")).get("department_name")
should work, dont?
Your variable value is of type Object, which means that the compiler will not know anything else about the variable. Even if the object you retrieve from your json file is a map, as you store it in a Object variable, the compiler will handle it as an Object and not as a Map. That is why you cannot do value.get("department"); : the method get does not exist for the type Object.
You need to cast whatever is stored in value.get("department") as a Map<String, Object> to be able to handle it as a Map.
I have found a special solution.
I convert json to Map<String,Map<String,Object>>.
Not in Map<String,Object>. In this case I can successfully use child-maps of parent dto-map.
But this solution is special (not general) in the meaning that, I can handle in this way json, which consist only of objects.
For example, if I try to get the value of "job" in following example:
{
"job": "prosecutor",
"department": {
"department_name":"prosecutor's office"
}
}
by using Map.Entry<String, Map<String, Object>> payloadNodeEntry.getValue,
I will receive ClassCastException (cant cast String to Map<String, Object> ).
I want to build a data aggregator which calls multiple services and extract some attributes from their response and then build the aggregated object.
Please consider the below example:
To fetch order details, I need to make a call to GetOrderDetails API of OrderService, which returns below output (simplified one):
{
statusCode: OK,
orderDetails: {
orderId: "order_id",
offer: {
offerId: "offer_id",
offerType: "offer_type"
},
address: {
addressId: "address_id",
houseNo: "house_no",
city: "city",
state: "state"
}
}
}
Similarly, I need to make a call other services.
Problem
I need to build output object by cherry picking the attributes from the response of these services without hard coding the logic through getters of the concerned attributes.
I am planning to have a list of needed attributes and their respective paths stored as config and then extracting logic would be generic.
Well, config will look like:
output_attribute: dotted_hierarchy_path
orderId: orderDetails.orderId
addressId: orderDetails.address.addressId
So, my output object will be:
{
orderId: <order_id>,
addressId: <address_id>
}
If I have this kind of configuration, my java code would be generic i.e. it can cherry pick any attribute from any object.
To extract the required attributes, I am thinking is to traverse the dotted path and through Java Reflection get the value. But Java Reflection is slow.
This is a practical world problem and hence want folks to put their valuable approaches.
As you said, reflection is slow and tricky. I wouldn't try to reimplement it from scratch but rather use existing libraries, such as FasterXML/jackson. And I wouldn't worry about performance for now.
In general, I would try to convert objects to nested maps. If you have access to plain jsons, you don't need to map them to objects, this way you skip reflection:
Map<String, Object> root = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {});
If you have to deal with objects, you can convert them to nested maps in a similar way:
Map<String, Object> root = objectMapper.convertValue(object, new TypeReference<Map<String, Object>>() {});
Getting values by dotted_hierarchy_path from the nested maps should be pretty easy.
Maybe a dumb question but how to update the content of a typed Map collection association of an exposed entity with Spring Data Rest using POST/PUT requests ?
I know how to POST/PUT on List or Set associations using Content-Type: text/uri-list but i don't with key/value Map<>
EDIT
I tried to send a ressource URI on the body of a PUT request with Content-Type: text/uri-list on the association endpoint. I gave no error, but it replaced all the previous content of the Map<> by this :
{
self: { ENTITY content}
}
So it seems to be supported (a side effect ?). It named the key self.
I don't know how to specify the key.
#WebService
#Path("/test")
public class TestResource {
#GET
#Produces(APPLICATION_JSON)
public HashMap<String, String> findAll() {
HashMap<String, String> test = new HashMap<>();
test.put("key1", "value1");
test.put("key2", "value2");
return test;
}
This produces JSON at the front end:
{"key2":"value2","key1":"value1"}
However this does use application/json content type which may not be what you are after?
I've been using Jackson for a while to parse json files and load the attribute and value into a Map. This is essentially what my code looks like:
Map<String, String> map = new HashMap<String, String>();
ObjectMapper mapper = new ObjectMapper();
File file = new File(pathToSource);
map = mapper.readValue(file, new TypeReference<HashMap<String, String>>() {});
This has worked well for flat json files where the keys are flat, just containing attribute/value pairs.
{
"attr":"value"
"attr":"value"
...
}
Now one of my sources has begun putting a key inside another key and th readValue method pukes when it hits the inner key.
{ "key1":{
"attr":"value"
"attr":"value"
"key2":{
"attr":"value"
"attr":"value"
}
}
}
One caveat of my need is I want to capture the attribute name and the value both. If I parse the json more granularly, taking attributes one by one, I can't access the attribute name.
I've been looking at this for a bit now and can't find the right combination to parse the keys, while capturing the attribute name and value.
Any suggestions welcome.
One thing to note is that if you just used simpler version:
Map<String, Object> map = mapper.readValue(file, Map.class);
you would get a Map that contains Strings, Lists and Maps as values, corresponding to matching JSON Structure (String, Array, Object).
You can generally use type java.lang.Object to mean "use the matching basic Java type", so signature you are asking for is Map<String,Object>, unless you want to enforce specific value, or use a POJO type.