Reading ExtJS writer root property using Jersey rest web service - java

I have successfully implemented Jersey for simple WebService calls (queryparams and simple objects) but when I try to sync a store or save a record, jersey doesn't understand the rootProperty of the writer. It doesn't know where to start and it cannot Consume the json record ExtJS is sending. Unfortunately rootProperty is mandatory according to ExtJS when you transform the data to JSON so I can't do without it. I can obviously use Consumes(MediaType.TEXT_PLAIN) and transform the object myself but I'm trying to take advantage of Jersey's automatic object marshalling etc.
What is the general practice used to .sync store data or .save records?
EDIT: I don't think the problem is with my object's JSON. My store's proxy is configured as follows:
proxy: {
type: 'ajax',
url: 'ext/AnnouncementHelper/myFunction',
headers: {'Content-Type': 'application/json;charset=utf-8'},
reader: {
type: 'json',
rootProperty: 'data',
messageProperty: 'processMessage'
},
writer: {
type: 'json',
rootProperty: 'data',
encode: true,
writeAllFields: true
}
What this does is create a POST request with the following parameter:
data={id: 1, description: 'status 1'}
My model is configured as follows:
public class AnnouncementStatus {
private int id;
private String description;
#JsonCreator
public AnnouncementStatus() {
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
My function declaration is:
#POST
#Path("myFunction")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response myFunction(AnnouncementStatus status)
This gives me the following error in java:
org.codehaus.jackson.JsonParseException: Unexpected character ('d' (code 100)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
I'm guessing jackson doesn't like that my object is starting with the data= but I cannot avoid this since it is mandatory to have a root property when using stores and records.

I found a solution to the problem I had although not to the question at hand.
I still have no idea how to read the writer's root property using jersey but I found out I don't need to anymore since if we change our proxy's writer to have encoding: false it is not mandatory to set a rootProperty and it just sends the records as an array. This is understood by Jersey's serializer and it can decode the data into objects correctly.

Related

How to send json with GET params via Spring Boot / Tomcat?

so currently I'm working on a project where we have product objects which in turn contain "Origin" objects (containing region: String and country: String).
What I'm trying to do is a RestController which takes in an optional Origin object and does something with it (e.g. logs it).
This is what I have right now:
#GetMapping("search")
public Page<Wine> getProductByStuff(
#RequestParam(required = false) Origin origin,
/* other attributes */) {
log.info(origin); // it has a proper toString method.
}
There are two problem with this approach. First of all, when I send a request like:
http://[...]/search?origin={"region":"blah","country":"UK"}
or even the html converted string like:
http://[...]/search?origin={%22region%22:%22blah%22%44%22country%22:%22UK%22}
... it says
Invalid character found in the request target [/api/products/search?origin={%22region%22:%22blah%22%44%22country%22:%22DE%22}]. The valid characters are defined in RFC 7230 and RFC 3986.
Afaik the only valid characters Tomcat has that I need are {}. All others I've replaced with the html encoded chars and it still doesn't work.
What I did to prevent this:
#Component
public class TomcatWebServerCustomizer
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Override
public void customize(TomcatServletWebServerFactory factory) {
TomcatConnectorCustomizer a = null;
factory.addConnectorCustomizers(connector -> {
connector.setAttribute("relaxedPathChars", "<>[\\]^`{|},\"");
connector.setAttribute("relaxedQueryChars", "<>[\\]^`{|},\"");
});
}
}
(See this, which is, by the way, deprecated (at least connector.setAttribute).)
This produced:
MethodArgumentConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type '[censored].backend.model.Origin'.
My questions are:
(How) is it possible to configure Tomcat/Spring so that they can actually accept json in the url params?
How would I format it in e.g. Postman so that it would work? Currently I'm just converting special characters by hand in the params tab of Postman.
Here is what you need to do if you want to send it as json query param.
#RestController
public class OriginController {
#GetMapping("/search")
public void getOrigin(#RequestParam(value = "origin", required = false)
Origin origin) {
System.out.println(origin);
}
}
Register a converter
#Component
public class StringToOriginConverter implements
Converter<String, Origin> {
ObjectMapper objectMapper = new ObjectMapper();
#Override
public Origin convert(String source) {
try {
return objectMapper.readValue(source, Origin.class);
} catch (JsonProcessingException e) {
//You could throw some exception here instead for custom error
return null;
}
}
}
Sending from postman
Note
My answer is not debating whether you should use POST or GET as it is not what you have asked. It is just providing one option if you want to send some payload as query param
As mentioned, don't use JSON as a path parameter.
Directly use path parameters, and convert to Origin object.
#GetMapping("search")
public Page<Wine> getProductByStuff(
#RequestParam(required = false) String region,
#RequestParam(required = false) String country, /* other attributes */) {
Origin origin = new Origin(region, country);
log.info(origin); // it has a proper toString method.
}

Convert json object to java object

I have a request like that:
let jsonData= {};
jsonData["className"]= className;
jsonData["models"]= arr;
let endPoint= "/classses?classAndModel=" + encodeURIComponent(JSON.stringfy(jsonData));
return $.ajax({
url: host + endPoint,
data: data,
cache: false,
contentType: false,
processData: false,
method: "POST"
});
I want to convert that json to java object.I tried this one
My rest service is:
#PostMapping(value=/classes",consumes=MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Void> addClassAndModelMapping(ClassAndModels classAndModels){
}
public class ClassAndModels {
ClassAndModelResult classAndModel;
...getter and setter...
}
public ClassAndModelResult {
String className;
List<String> models;
...getter and setters...
}
I get 400 error.If I change that line ClassAndModelResult classAndModel to String classAndResult.I get response but I want Object type.Do you have any idea?
The first part of code shows that you are sending data as a query string.
Take a look at https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html
But considering the #PostMapping, you should send that data in the request body and do something like this on the server side.
#PostMapping("/classes")
public ResponseEntity<Void> addClassAndModelMapping(#RequestBody ClassAndModels classAndModels){
//
}
As Phils says, you can add a GetMapping on your controller to see how your ClassAndModels its being serialized
Source: https://spring.io/guides/tutorials/bookmarks/
P.S. Sorry about my english, I'm not a native speaker.
Please try to add #RequestParam annotation or better use classAndModel value as RequestBody similar to the below.And also correct the spelling mistake in the javascript url.
#PostMapping(value = "/classes")
public ResponseEntity<Void> addClassAndModelMapping(#RequestBody ClassAndModels modal) {
}

Wrap incoming jax-rs POST json message body in a parent object

I have a need to parse some json that comes to an endpoint of mine. I cannot change the incoming json, it is sent by a 3rd party. The json in the request body is technically valid but it has no parent element so I can't seem to parse it. Ideally I'd be able to wrap the whole thing in an object, so basically, how can I actually add the "wrapper" object:
{
"wrapper": {
{
"value1": 1,
"value2": 2,
"value3": 3
}
}
}
if the original was:
{
"value1": 1,
"value2": 2,
"value3": 3
}
With that I could make a model and use xml annotation as I have elsewhere in the project, something like this:
#XmlRootElement(name = "wrapper")
#XmlAccessorType(XmlAccessType.NONE)
public class WrapperObject {
#XmlElement(name = "value1")
private int value1;
#XmlElement(name = "value2")
private int value2;
#XmlElement(name = "value3")
private int value3;
}
And then I could have the rest method be
#POST
#Path("/cloud")
#Override
public Response handleResponse(WrapperObject wrapper) throws Exception {
}
As your question is an answer, my answer will be a question. What do you ask? p.s. your idea and code look fine. When a project is spread between Front end and Back end teams they should always have a CONTRACT about the data transfer objects so parsing is possible for the both sides.

Sending JSON to server is giving 415 Unsupported Media Type

I have the following angular code:
<button ng-click="ctrl.submit(light)">Switch</button>
and the button click is handled by:
self.submit = function(light) {
console.log("==============================");
console.log("clicked button: ", light);
console.log("==============================");
$http.post('/ARHomeAutomation/rest/light/state/', light).then(function(response) {
console.log('headers: ' , response.headers);
console.log('status: ', response.status);
console.log('config: ', response.config);
console.log('data: ', response.data);
self.state = response.data;
}, function(errResponse) {
console.error('Error while updating light state');
})
console.log('User clicked submit with: ', light.id );
console.log('response: ', self.light);
}
On the server side I have the following method that should respond to the request:
#POST
#Path("/state/")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_HTML)
public String setStateOfLight(JSONObject aLight) {
if(aLight == null) {
return "It's a null";
} else {
System.out.println("content: " + aLight);
}
JSONObject jsonObject = new JSONObject();
//Update the state of the light with the given id
for(Light light: lightCollection) {
// .....
}
return getLightsStateAsJSON();
}
When I click my button I get the following message:
According to firefox I do send JSON in my request. At least it says so when I examine the send data.
This is my header request data:
What am I missing here?
"I downloaded Jersey 2.17 with all dependencies. AT least that what it says on their download site"
Yeah so the Jersey distribution (Jersey JAX-RS 2.0 RI bundle ) doesn't come bundled with an JSON conversion support, besides basic low level types, that can be converted from an InputStream. So without anything else beside the jars that come in that Jersey Bundle, the only type you have use are String, InputStream and byte[]. Doesn't really help much if you are trying to manipulate the JSON
How conversion works is through the use of MessageBodyReaders and MessageBodyWriters. I don't know what JSONObject is, but I'm guessing it's from here. In any case, you will need a MessageBodyReader for it to handle the incoming conversion. My guess is you don't have one. I personally don't know where to get one for that specific API.
I would instead make use of a library that can handle JSON to POJO mapping. Jackson is the one I would recommend. The basic are pretty easy to understand. Say this is your JSON
{"name":"your name", "id": "2"}
All you need to do is create a class with fields an properties, using JavaBean naming convention. So for the above JSON, we could use this
public class User {
private String name;
private String id;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getId() { return id; }
public void setId(String id) { this.id = id; }
}
Now your method parameter can accept a User type.
To get this support, you need to add the Jackson provider. Download the below
jersey-media-json-jackson
Get all the one from the below image
I had an image from another post with v2.2.3- disregard the version. The version you want to get 2.3.2 for all of them. They can all be found at the above link, for the first dependency. Just search for them in the search bar. When you find it, select the version and download it.
After adding these jars. you should have JSON to POJO support.
It appears that the header content-type is set to text/html, even though the body is JSON. I imagine you will need to set the content-type to application/json somewhere in the post in the Angular code.

json and Spring MVC

I have trouble with Spring MVC and json.
I use SimplecartJS to generate json data like this :
{"currency":"RUR",
"shipping":250,
"tax":0,
"taxRate":0,
"itemCount":2,
"item_name_1":"Name of product #1",
"item_quantity_1":6,
"item_price_1":159,
"item_options_1":"",
"item_name_2":"Name of product #2",
"item_quantity_2":2,
"item_price_2":159,
"item_options_2":"",
"form":{
"Fname":"UserName",
"Phone":"7123456789",
"Address":"UserAddress",
"Comment":"Comment Text"
}
}
My controller Spring
#RequestMapping(value = "/checkorder2", method = RequestMethod.POST, headers = "Accept=application/json")
public String test (#RequestBody OrderCon orderC)throws Exception{
ObjectMapper om = new ObjectMapper();
om.canSerialize(OrderCon.class);
System.out.println(om);
return test(orderC);
}
Code from client side
var url = 'http://localhost:8080/url'
jQuery.ajax({
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
contentType:'application/json',
dataType: "json",
type: "POST",
url: url,
data: JSON.stringify(data),
And my question:
When I send data to controller, I have a mistake 400 Bad request. The request sent by the client was syntactically incorrect. When adding a new item field name will "item_name_2","item_name_3" etc. How I can parse this. I try parse to List, Set but it is not working. Please help.
UPD1: OrderCon.java
public class OrderCon {
private List<String> form;
private List<List<String>> json;
getters and setters...
}
There is no way you are going to be able to parse that data to a class like you have, since you need to add dynamic properties to a Java class.
You do however have two options to get the data out
First option is parse the json to a HasMap of Strings, ie. change your controller signature to
public String test (#RequestBody Map<String, String> orderC)
The other option is to use a JsonNode and deal with the data as a tree, here is an example http://wiki.fasterxml.com/JacksonTreeModel
I personally would try the latter first
You're getting 400 Bad Request because Spring is trying to map the passed JSON directly to your OrderCon class.
For this to work, the class would need to map to the keys specified in the JSON.
IE:
JSON:
{
"name" : "foo",
"phone": "111-111-1111"
}
would map to:
public class someJsonPojo(){
String name;
String phone;
//setters & getters
}
I don't think this will work well since instead of getting an array of items, you only get an item appended to the list as a new key:value. You should be able to modify the JSON so that you can map to an array of Item objects, which contain the name, quantity, price, etc.

Categories