can Rest API parse hashmap<string,object>? - java

i would like to know if rest api while consuming input parameter can do the following:
let's say my json object have the following parameters:
string name;
string adress;
hashmap<string,object> content;
and here's an exemple of what can be sent:
{
"name": "AZ",
"adress": "US",
"content": {
"clients": [
{
"client_ref":"213",
"commands" : {
"subCommands": [
{
"num":"1",
"price":"10euro"
},
{
"num":"12,
"price":"10euro"
}
]
}
},
{
"client_ref":"213",
"commands" : {
"subCommands": [
{
"num":"1",
"price":"10euro"
},
{
"num":"12,
"price":"10euro"
}
]
}
}
]
}
}
the question is can rest build the hashmap where the object can itself have n child of hashmap type ... ?
(i'm using jersey as rest implementation )

Assuming that you have a JSON provider such as Jackson registered and your model class looks like:
public class Foo {
private String name;
private String address;
private Map<String, Object> content;
// Getters and setters
}
The following resource method:
#Path("foo")
public class Test {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response post(Foo foo) {
...
}
}
Can handle a request like:
POST /api/foo HTTP/1.1
Host: example.org
Content-Type: application/json
{
"name": "AZ",
"adress": "US",
"content": {
"clients": [
{
"client_ref": "213",
"commands": {
"subCommands": [...]
}
},
{
"client_ref": "213",
"commands": {
"subCommands": [...]
}
}
]
}
}

content is an Object, not a map.
"content": {
"clients": [
{
"client_ref":"213",
"commands" : {
"subCommands": [
{
"num":"1",
"price":"10euro"
},
{
"num":"12,
"price":"10euro"
}
]
}
},
{
"client_ref":"213",
"commands" : {
"subCommands": [
{
"num":"1",
"price":"10euro"
},
{
"num":"12,
"price":"10euro"
}
]
}
}
]
}
And this is Java Object presentation.
public class Content {
private List<Client> clients;
//Getters and setters
}
public class Client {
private String clientRef;
private List<Command> commands;
//Getters and setters
}
//And so on, define other classes.
To answer your question, yes, you can build a map.
Check this example, please. It tells how to parse an unknown json (in case you don't know the exact structure of your json object).
https://stackoverflow.com/a/44331104/4587961
Then you can build a map with fields
Map<String, Object> where some values of this map will be nested maps.

you can use javax.ws.rs.core.GenericEntity to wrap collections with generic types (your HashMap).
#GET
#Path("/mapping")
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Response getAllMapContents() {
Map<String,Object> map = new HashMap<String,Object>();
map.put("Hello", "World");
map.put("employee", new Employee(1,"nomad"));
GenericEntity<Map<String,Object>> entity = new GenericEntity<Map<String,Object>>(map) {};
return Response.ok(entity).build();
}
I checked it and found it working Please find the response below. Thank you.
{
"Hello": "World",
"employee": {
"id": 1,
"name": "nomad"
}
}

Related

Jackson- How to process the request data passed as JSON (Netsed Json)?

{
"DistributionOrderId" : "Dist_id_1",
"oLPN":
{
"Allocation":
{
"AID": "12345"
},
"Allocation":
{
"AID": "123456" ,
"SerialNbr": "SRL001",
"BatchNbr": "LOT001"
"RevisionNbr": "RVNBR1"
}
},
"oLPN":
{
"Allocation":
{
"AID": "12123"
}
"Allocation":
{
"AID": "12124"
}
}
}
I have a JSON request passed from the vendor, How to store the values as Java POJO and use them further?
Edit : Added attributes to JSON
As you aren't using the usual camel case conventions common in jackson databinding I would suggest defining a java object with the appropriate annotations. The object is not currently valid json. If for example the json looks like this:
{
"DistributionOrderId" : "Dist_id_1",
"oLPN": [ ... ]
}
Where each oLPN in the array is an object like this:
{
"Allocation": [{ "AID": ... }, { "AID": ... }, ... ]
}
You can write a class for distribution orders
public class DistributionOrder {
#JsonProperty("DistributionOrderId") private String id;
#JsonProperty("oLPN") private List<OLPN> olpn;
// getters and setters
}
Then another for the OLPN object
public class OLPN {
#JsonProperty("Allocation") private String allocation;
#JsonProperty("AID") private String aid;
// getters and setters
}
Then you can use an object mapper as appropriate. For example
ObjectMapper mapper = ... // get your object mapper from somewhere
DistributionOrder distributionOrder = mapper.readValue(raw, DistributionOrder.class);
See also object mapper javadoc
json key can't repeat, and the repeat key with value will be discard.
i have format your json text, may be it is like following expression:
{
"DistributionOrderId" : "Dist_id_1",
"oLPN":
[
{
"Allocation":
[
{
"AID": "123456" ,
"SerialNbr": "SRL001",
"BatchNbr": "LOT001",
"RevisionNbr": "RVNBR1"
},
{
"AID": "12345"
}
]
},
{
"Allocation":
[
{
"AID": "12123"
},
{
"AID": "12124"
}
]
}
]
}
and this struct match the java object is
class DistributionOrder{
#JsonProperty("DistributionOrderId")
String distributionOrderId;
List<OLPN> oLPN;
}
class OLPN {
#JsonProperty("Allocation")
List<Allocation> allocation;
}
class Allocation{
String AID;
#JsonProperty("SerialNbr")
String serialNbr;
#JsonProperty("BatchNbr")
String batchNbr;
#JsonProperty("RevisionNbr")
String revisionNbr;
}

How to fix Gson JSON parsing from LinkedTreeMap to ArrayList?

I'm using Retrofit+Gson for parsing JSON.
When I try parse response from Google Places API (ok, I don't try parse, I just try to make model for this response) and I get some error.
This is response from Google Place API:
{
"predictions" : [
{
"description" : "Николаевская область, Украина",
"id" : "3bd747cc4efc2288da48942b909ce18a053c2060",
"matched_substrings" : [
{
"length" : 5,
"offset" : 0
}
],
"place_id" : "ChIJydRVsbqaxUARLq1R8Q3RgpM",
"reference" : "ClRPAAAAwseWiG8NUMt7TqSqz9rMP8R2M4rX7-cMRmIp4OCYL-VdRSr5B5T_PMwWzYOydVStVpYDvm0ldXYPEzxFAuvn1LqhtWHdROhsERwvmx0tVlwSEFdMw0sOe3rDaB2AqKKmF-YaFLvhiEOz3Bklv5-iTa7QQORILVCU",
"structured_formatting" : {
"main_text" : "Николаевская область",
"main_text_matched_substrings" : [
{
"length" : 5,
"offset" : 0
}
],
"secondary_text" : "Украина"
},
"terms" : [
{
"offset" : 0,
"value" : "Николаевская область"
},
{
"offset" : 22,
"value" : "Украина"
}
],
"types" : [ "administrative_area_level_1", "political", "geocode" ]
}, ...],
"status" : "OK"
}
This is my model for this response:
public class GetGoogleMapPlacesResponse {
#SerializedName("predictions")
private List<GooglePlace> googlePlaces;
public List<GooglePlace> getGooglePlaces() {
return googlePlaces;
}
public void setGooglePlaces(List<GooglePlace> googlePlaces) {
this.googlePlaces = googlePlaces;
}
}
But when Retrofit try's to parse response to model I get error:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.myapp.net.rest.response.GetGoogleMapPlacesResponse
And this is raw response in Debug mode:
You're missing a constructor of GetGoogleMapPlacesResponse model.
public class GetGoogleMapPlacesResponse {
private List<GooglePlace> googlePlaces;
private String status;
public GetGoogleMapPlacesResponse(List<GooglePlace> googlePlaces, String status) {
this.googlePlaces = googlePlaces;
this.status = status;
}
...getters & setters
}
But i highly suggest you to use AutoValue with Gson extension and then your model will look like this :
#AutoValue
public abstract class GetGoogleMapPlacesResponse {
#SerializedName("predictions") public abstract List<GooglePlace> googlePlaces;
public abstract String status;
}
For more info look here : https://github.com/rharter/auto-value-gson

Deserialize JSON to POJO using Retrofit and Gson

I am working with an API that responds like the following for a single user resource:
{
"data": {
"id": 11,
"first_name": "First",
"last_name": "Last",
"books": {
"data": [
{
"id": 13,
"name": "Halo"
}
]
},
"games": {
"data": [
{
"id": 1,
"name": "Halo"
}
]
}
}
}
or like the following for multiple user resources:
{
"data": [
{
"id": 11,
"first_name": "First",
"last_name": "Last",
"books": {
"data": [
{
"id": 13,
"name": "Halo"
}
]
},
"games": {
"data": [
{
"id": 1,
"name": "Halo"
}
]
}
},
],
"meta": {
"pagination": {
"total": 11,
"count": 10,
"per_page": 10,
"current_page": 1,
"total_pages": 2,
"links": {
"next": "http://api.###.com/users?page=2"
}
}
}
}
Key things to notice are:
all resources are nested under a data key, single as an object or multiple as an array of objects. This includes nested resources such as books and games in the example above.
I need to be able retrieve the values of the meta key for my pagination routines
User model
public class User extends BaseModel {
public Integer id;
public String firstName;
public String lastName;
public List<Book> books; // These will not receive the deserialized
public List<Game> games; // JSON due to the parent data key
}
Custom JSON deserializer
public class ItemTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
// If the data key exists and is an object or array, unwrap it and return its contents
if (jsonObject.has("data") && (jsonObject.get("data").isJsonObject() || jsonObject.get("data").isJsonArray())) {
jsonElement = jsonObject.get("data");
}
}
return delegate.fromJsonTree(jsonElement);
}
}.nullSafe();
}
}
This is all working fine but I can't figure out how to access the meta key for pagination.
Ideally I would get Gson to deserialize the response to the following POJO:
public class ApiResponse {
public Object data;
public Meta meta
}
and I could just cast the response field to the correct type in the response callback like the following:
Map<String, String> params = new HashMap<String, String>();
params.put("include", "books,games");
ApiClient.getClient().authenticatedUser(params, new ApiClientCallback<ApiResponse>() {
#Override
public void failure(RestError restError) {
Log.d("TAG", restError.message);
}
#Override
public void success(ApiResponse response, Response rawResponse) {
User user = (User) response.data; // Cast data field to User type
Log.d("TAG", user.firstName);
Log.d("TAG", "Total pages" + response.meta.pagination.total.toString()); // Still have access to meta key data
}
});
However the data field of the ApiResponse object is null.
My Java is very rusty and I have no idea if this is even possible nor do I understand how to go about it correctly, any help would be much appreciated.
I needed the same thing and have managed to get it working by adding an if statement to your custom serializer:
…
// If the meta key exists, consider the element to be root and don't unwrap it
if (!jsonObject.has("meta")) {
// If the data key exists and is an object or array, unwrap it and return its contents
if (jsonObject.has("data") && (jsonObject.get("data").isJsonObject() || jsonObject.get("data").isJsonArray())) {
jsonElement = jsonObject.get("data");
}
}
…
The reason why the data field of your ApiResponse was null is because your original deserializer was processing the whole response and making you "loose" the root object's data and meta elements.
I've also parametized the ApiResponse class:
public class ApiResponse<T> {
public Meta meta;
public T data;
}
That way deserializing still works without creating many different Response classes, while casting isn't needed anymore and you can specify the type of ApiResponse's data field as needed (eg. ApiResponse<User> for single user resource, ApiResponse<List<User>> for multiple user resources, etc.).

Create complex json objects using gson

How to create javabean for gson for the below JSON script?
{
"header": [
{
"title": {
"attempts": 3,
"required": true
}
},
{
"on": {
"next": "abcd",
"event": "continue"
}
},
{
"on": {
"next": "",
"event": "break"
}
}
]
}
I'm trying to build the javabean for this JSON output. I'm not able to repeat the fieldname on.
Please suggest any solutions.
You will need multiple classes to accomplish this. I made some assumptions with the naming, but these should suffice:
public class Response {
private List<Entry> header;
private class Entry {
private Title title;
private On on;
}
private class Title {
int attempts;
boolean required;
}
private class On {
String next, event;
}
}
You can test it with a main() method like:
public static void main(String[] args) {
// The JSON from your post
String json = "{\"header\":[{\"title\":{\"attempts\":3,\"required\":true}},{\"on\":{\"next\":\"abcd\",\"event\":\"continue\"}},{\"on\":{\"next\":\"\",\"event\":\"break\"}}]}";
Gson gson = new GsonBuilder().setPrettyPrinting().create();
Response response = gson.fromJson(json, Response.class);
System.out.println(response.header.get(0).title.attempts); // 3
System.out.println(response.header.get(1).on.next); // abcd
System.out.println(gson.toJson(response)); // Produces the exact same JSON as the original
}

Jackson: Returning just part of the response

Currently service response looks like this:
{
"values": [
{
"field1": "value",
.................
},
{
"field1": "value",
.................
}
]
metadata1:[],
metadata2:"-"
}
But just want to send array of values as response. Like this
[
{
"field1": "value",
.................
},
{
"field1": "value",
................
}
]
I am able to suppress metadata info with the help of #JsonIgnoreProperties. But response still like this:
{
"values": [
{
"field1": "value",
.................
},
{
"field1": "value",
.................
}
]
}
How can I fix it?
It is easy to achieve using the #JsonValue annotation which can change the representation of the of your java class. Just add a method annotated with this annotation to your response class that returns the values collection. Here is an example:
public class JacksonValue {
public static class Bean {
private final List<String> values;
public Bean(List<String> values) {
this.values = values;
}
#JsonValue
public List<String> getValues() {
return values;
}
}
public static void main(String[] args) throws JsonProcessingException {
Bean bean = new Bean(Arrays.asList("a", "b", "c"));
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(bean));
}
}
Output:
["a","b","c"]

Categories