Post nested Json to spring controller using Jackson data binding - json

Well I am trying to retrieve a nested json in spring controller and getting "The request sent by the client was syntactically incorrect."
My code is working and getting the correct data binding if I don't do nested json format, so I can conclude that maybe something is not right in my DTO.
CURL Command:
CURL -i -H "Content-Type: application/json" -X POST http://localhost:8080/insertMapping -d '{"mapping": {"adobe_segment_id": "125", "dp_key_id": "1"}, "type": "adobe"}'
JSON:
{"mapping": {"adobe_segment_id": "125", "dp_key_id": "1"}, "type": "adobe"}
Controller:
#RequestMapping(value = "/insertMapping", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> createUser(#RequestBody RequestBodyDTO mapping) {
LOG.info("/insertMapping" + " ,type:" + mapping.getType().getType());
return null;
}
RequestBodyDTO:
public class RequestBodyDTO {
private MappingDTO mapping;
private TypeDTO type;
public TypeDTO getType() {
return type;
}
public void setType(TypeDTO type) {
this.type = type;
}
public MappingDTO getMapping() {
return mapping;
}
public void setMapping(MappingDTO mapping) {
this.mapping = mapping;
}
}
MappingDTO:
public class MappingDTO {
// adobe
private Integer adobe_segment_id;
private Integer dp_key_id;
public Integer getAdobe_segment_id() {
return adobe_segment_id;
}
public void setAdobe_segment_id(Integer adobe_segment_id) {
this.adobe_segment_id = adobe_segment_id;
}
public Integer getDp_key_id() {
return dp_key_id;
}
public void setDp_key_id(Integer dp_key_id) {
this.dp_key_id = dp_key_id;
}
}
TypeDTO:
public class TypeDTO {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

Problem resolved after I changed "private TypeDTO type" to "private String type".

Related

#RestController custom ResponseEntity - Jackson

What i'm trying to achieve here is to get a custom response from the RequestMapping, below is the structure of the json which I'm trying to get in case of an array of objects:
{
"error": false,
"message": "the message",
"data": [{},{},...]
}
and the below in case of object
{
"error": false,
"message": "the message",
"data": {}
}
The code is working fine but the problem is "data" will not always has an array, it may store an object, so what I tried is to create a custom POJO class which contains my custom response and when I want to annotate two attributes with same name i'm getting the below error
Could not find acceptable representation
And what if I create another class which will contain the same attributes but with an JsonObject not with array, is there any better way to achieve this ?
Below are my classes :
#JsonInclude(Include.NON_NULL)
public class JsonResponseObject<T> implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private boolean error;
private String message ;
#JsonProperty(value="data")
private ArrayList<T> array;
#JsonProperty(value="data")
private Object object ;
public JsonResponseObject() {
}
public boolean isError() {
return error;
}
public void setError(boolean error) {
this.error = error;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public ArrayList<T> getArray() {
return array;
}
public void setArray(ArrayList<T> array) {
this.array = array;
}
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}
UserJsonController.java :
#RestController()
#RequestMapping(value = "/json")
public class UserJsonController {
#Autowired
private UserRepository userDAO;
#RequestMapping(value = "/users", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getUsers() {
ArrayList<Users> entityList = (ArrayList<Users>) userDAO.findAll();
JsonResponseObject<Users> jsonResponse = new JsonResponseObject<Users>();
jsonResponse.setError(false);
jsonResponse.setMessage("test");
jsonResponse.setArray(entityList);
return new ResponseEntity<>(jsonResponse, HttpStatus.OK);
}
#RequestMapping(value = "/users/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getUserByID(#PathVariable int id) {
JsonResponseObject<Users> jsonResponse = new JsonResponseObject<Users>();
jsonResponse.setError(false);
jsonResponse.setMessage("test");
jsonResponse.setObject(userDAO.findById(id).get());
return new ResponseEntity<>(jsonResponse, HttpStatus.OK);
}}

how to accept a List<List<String>> from json in rest web services

I have json input as
{"subkeys":[{"1","2", "3"},{"4","5","6"},{"7","8","9"}]
I need to get this as List < List < String > > or List < String[] >.
#PUT
#Path("/{key}/")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public MyResponse loadData( List<List<String>> subkeys) {
}
I got the error like
Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
java.util.List is an interface, and JAXB can't handle interfaces.
this problem is related to the following location:
at java.util.List
at private java.util.List foobar.alkohol.register.webservice.jaxws.GetRelationsFromPersonResponse._return
The problem I can see here is due to the wrong format of the JSON input, the correct JSON in your case is:
{
"data" :[
{
"keys": ["123","456","789"],
"subkeys": [
["1", "2", "3"],
["1", "2", "3"],
["1", "2", "3"]
]
}
]
}
try with this and let know.
my code is as follows
#PUT #Path("/{key}/")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public MyResponse loadData( MonitoringDataRequest monReq) {
}
#XmlRootElement(name="data")
public class MonitoringDataRequest {
private List<MonitoringData> data;
#XmlElement
public void setData(List<MonitoringData> data) {
this.data = data;
}
public List<MonitoringData> getData() {
return data;
}
}
#XmlRootElement(name="monitoring_data")
public class MonitoringData {
private List<String> keys;
private List<SubkeyData> subKeys;
#XmlElement
public void setKeys(List<String> keys) {
this.keys = keys;
}
public List<String> getKeys() {
return keys;
}
#XmlElement(name="subkeys")
public void setSubKeys(List<SubkeyData> subKeys) {
this.subKeys = subKeys;
}
public List<SubkeyData> getSubKeys() {
return subKeys;
}
}
#XmlRootElement(name="subkeys")
//#XmlAccessorType(XmlAccessType.NONE)
public class SubkeyData
{
private List<String[]> subkeys;
#XmlElement
public void setSubkeys(List<String[]> subkeys)
{
this.subkeys = subkeys;
}
public List<String[]> getSubkeys()
{
return subkeys;
}
}

MOXyJsonProvider bug when unmarshalling json array

I have a simple jersey resource, UserContentManager, that processes a simple ContentInput class. Both classes are below. The postHello method works fine when called using curl -X POST -H "Content-Type: application/json" -d '{"id":1,"type":"a"}' localhost:50000/news/rest/hello but the putHello method is failing when called with curl -X PUT -H "Content-Type: application/json" -d '[{"id":1}]' localhost:50000/news/rest/hello
It is failing in MOXyJsonProvider:598 (the line in bold below) because when it is unmarshalled, it is unmarshalled to ArrayList<Property> instead of ArrayList<JAXBElement<Property>> as the code is expecting i.e. Object value = jaxbElement.getValue() is an ArrayList<Property> not ArrayList<JAXBElement> like the cast.
Is this a bug in Moxy or am I doing something wrong? The getArray method is working fine when returning an array. I have tried it with and without #XmlRootElement on the ContentInput class but the results are the same.
JAXBElement<?> jaxbElement = unmarshaller.unmarshal(jsonSource, domainClass);
if(type.isAssignableFrom(JAXBElement.class)) {
return jaxbElement;
} else {
Object value = jaxbElement.getValue();
if(value instanceof ArrayList) {
if(type.isArray()) {
ArrayList<JAXBElement> arrayList = (ArrayList<JAXBElement>) value;
int arrayListSize = arrayList.size();
Object array;
if(genericType instanceof GenericArrayType) {
array = Array.newInstance(JAXBElement.class, arrayListSize);
for(int x=0; x<arrayListSize; x++) {
Array.set(array, x, arrayList.get(x));
}
} else {
array = Array.newInstance(domainClass, arrayListSize);
for(int x=0; x<arrayListSize; x++) {
* Array.set(array, x, arrayList.get(x).getValue());*
}
}
return array;
#WebService
#Path("/hello")
public class UserContentManager {
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response postHello(ContentInput input) {
input.setId(input.getId());
input.setType("clip" + input.getType());
ResponseBuilder builder = Response.ok();
builder.entity(input);
return builder.build();
}
#PUT
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public ContentInput[] putHello(ContentInput [] input) {
return input;
}
#Path("/array")
#GET
#Produces(MediaType.APPLICATION_JSON)
public ContentInput[] getArray() {
return new ContentInput[] {
new ContentInput(),
new ContentInput()
};
}
}
public class ContentInput {
private int id;
private String type;
public ContentInput() {}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
You are hitting the following bug which we have fixed in EclipseLink 2.5.1:
http://bugs.eclipse.org/413760

GSON throwing “Expected BEGIN_OBJECT but was STRING

This is Json Object
[
{
"UserId":"demouser1",
"Catagories":[
{
"CatagoryName":"Entertainment",
"Persent":"25"
},
{
"CatagoryName":"Household",
"Persent":"25"
},
{
"CatagoryName":"Movie",
"Persent":"25"
},
{
"CatagoryName":"Misc",
"Persent":"25"
}
],
"RequestId":null,
"ResponseStatus":false,
"Token":null
}
]
Used The Following approach to parse the above Json
public class CategoryEntity {
private String CatagoryName;
private String Persent;
public String getCatagoryName() {
return CatagoryName;
}
public void setCatagoryName(String catagoryName) {
CatagoryName = catagoryName;
}
public String getPersent() {
return Persent;
}
public void setPersent(String persent) {
Persent = persent;
}
}
import java.util.List;
public class Entity {
private String UserId;
public String getUserId() {
return UserId;
}
public void setUserId(String userId) {
UserId = userId;
}
public List<CategoryEntity> getListCatagories() {
return ListCatagories;
}
public void setListCatagories(List<CategoryEntity> listPMMCatagories) {
ListCatagories = listPMMCatagories;
}
public String getRequestId() {
return RequestId;
}
public void setRequestId(String requestId) {
RequestId = requestId;
}
public boolean isResponseStatus() {
return ResponseStatus;
}
public void setResponseStatus(boolean responseStatus) {
ResponseStatus = responseStatus;
}
private List<CategoryEntity> ListCatagories;
private String RequestId;
private String Token;
public String getToken() {
return Token;
}
public void setToken(String token) {
Token = token;
}
private boolean ResponseStatus;
}
And
Following approach to convert the json object to corresponding object
Gson gson =new Gson();
JsonPrimitive listCatagoriesElement= element.getAsJsonPrimitive();
System.out.println("listCatagoriesElement.getAsString()>>"+listCatagoriesElement.getAsString());
sysout prints: listCatagoriesElement.getAsString()>>[{"UserId":"user1","ListCatagories":[{"CatagoryName":"Entertainment","Persent":"25"},{"CatagoryName":"Household","Persent":"25"},{"CatagoryName":"Movie","Persent":"25"},{"CatagoryName":"Misc","Persent":"25"}],"RequestId":null,"ResponseStatus":false,"Token":null}]
Entity entity = gson.fromJson(listCatagoriesElement, Entity.class);
Any ideas how should I fix it?
Thanks!
Your class CategoryEntity is correct, but in your class Entity, the attribute ListCatagories should be called Catagories to match the name in the JSON!
Apart from that, in order to parse the JSON you'd better do something like this:
Gson gson = new Gson();
Type listType = new TypeToken<List<Entity>>() {}.getType();
List<Entity> entities = gson.fromJson(yourJsonString, listType);
So you'll have a List containing just one Entity object, and you can access the values just with:
String catagoryNameI = entities.get(0).getCatagories().get(i).getCatagoryName();
String persentI = entities.get(0).getCatagories().get(i).getPersent();
You have to do this because your whole JSON response is an array, surrounded by [ ... ], so you need to parse it into some List...

Simple way to strip outer array of responses in gson

I'm working with an api (Phillips Hue) that wraps all of it's json responses in an array with one entry (the content).
Example:
[{
"error": {
"type": 5,
"address": "/",
"description": "invalid/missing parameters in body"
}
}]
I usually write standard POJO's parsed by GSON to handle responses but since the response is not a json object I'm a bit stumped on the best way to deal with this. I didn't really want every object to actually be an array that I have to call .get(0) on.
Example of the POJO if it was a JSON obj and NOT wrapped in an array.
public class DeviceUserResponse {
private DeviceUser success;
private Error error;
public DeviceUser getSuccess() {
return success;
}
public Error getError() {
return error;
}
public static class Error {
private int type;
private String address;
private String description;
public int getType() {
return type;
}
public String getAddress() {
return address;
}
public String getDescription() {
return description;
}
#Override
public String toString() {
return "Type: " + this.type
+ " Address: " + this.address
+ " Description: " + this.description;
}
}
}
What I have to do right now:
ArrayList<DeviceUserResponse> response.get(0).getError();
Is there a way that I can strip this array for every response or am I just going to have to do a .get(0) in my POJO's and just not expose it?
I think you've to go with custom deserialization in order to "strip out" the array.
Here a possible solution.
An adapter for your response POJO:
public class DeviceUserResponseAdapter extends TypeAdapter<DeviceUserResponse> {
protected TypeAdapter<DeviceUserResponse> defaultAdapter;
public DeviceUserResponseAdapter(TypeAdapter<DeviceUserResponse> defaultAdapter) {
this.defaultAdapter = defaultAdapter;
}
#Override
public void write(JsonWriter out, DeviceUserResponse value) throws IOException {
defaultAdapter.write(out, value);
}
#Override
public DeviceUserResponse read(JsonReader in) throws IOException {
in.beginArray();
assert(in.hasNext());
DeviceUserResponse response = defaultAdapter.read(in);
in.endArray();
return response;
}
}
A factory for your adapter:
public class DeviceUserResponseAdapterFactory implements TypeAdapterFactory {
#Override
#SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (type.getRawType()!=DeviceUserResponse.class) return null;
TypeAdapter<DeviceUserResponse> defaultAdapter = (TypeAdapter<DeviceUserResponse>) gson.getDelegateAdapter(this, type);
return (TypeAdapter<T>) new DeviceUserResponseAdapter(defaultAdapter);
}
}
Then you've to register and user it:
DeviceUserResponseAdapterFactory adapterFactory = new DeviceUserResponseAdapterFactory();
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.registerTypeAdapterFactory(adapterFactory).create();
DeviceUserResponse response = gson.fromJson(json, DeviceUserResponse.class);
System.out.println(response.getError());
This solution will not work if you have the DeviceUserResponse inside other complex JSON object. I that case the adapter will try to find an array and will terminate with an error.
Another solution is to parse it as array and then in your "communication" layer you get only the first element. This will preserve the GSon deserialization.
In the comment you're asking for a more generic solution, here one:
The adapter:
public class ResponseAdapter<T> extends TypeAdapter<T> {
protected TypeAdapter<T> defaultAdapter;
public ResponseAdapter(TypeAdapter<T> defaultAdapter) {
this.defaultAdapter = defaultAdapter;
}
#Override
public void write(JsonWriter out, T value) throws IOException {
defaultAdapter.write(out, value);
}
#Override
public T read(JsonReader in) throws IOException {
in.beginArray();
assert(in.hasNext());
T response = defaultAdapter.read(in);
in.endArray();
return response;
}
}
The factory:
public class ResponseAdapterFactory implements TypeAdapterFactory {
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if ((type.getRawType().getSuperclass() != Response.class)) return null;
TypeAdapter<T> defaultAdapter = (TypeAdapter<T>) gson.getDelegateAdapter(this, type);
return (TypeAdapter<T>) new ResponseAdapter<T>(defaultAdapter);
}
}
Where Response.class is your super class from which all the service responses inherit.
The first solution advices are still valid.