I have to update my resources partially using PATCHrequest whose body is a JSON. Below is my POJO for OwnerDetails. I am using play-framework with Hibernate.
public class OwnerDetailsVO {
private int id;
private String name;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
I have created rows in MySQL for the Entity Object which corresponds to this value object (VO).
My JSON body for PATCH request is,
PATCH /owners/123
[
{ "op": "replace", "path": "/name", "value": "new name" }
]
I have configured the correct route to the method in the routes file.
Here is the OwnerController class which should process the JSON request. I am using POSTMAN to send the requests.
public class OwnerController extends Controller {
public Result create() {
Form<OwnerDetailsVO> odVOForm = Form.form(OwnerDetailsVO.class).bindFromRequest();
if(odVOForm.hasErrors()) {
return jsonResult(badRequest(odVOForm.errorsAsJson()));
}
OwnerDetailsVO odVO = odVOForm.get();
int id = odProcessor.addOwnerDetails(odVO);
return jsonResult(ok(Json.toJson("Successfully created owner account with ID: " + id)));
}
public Result update(int id) {
//I am not sure how to capture the data here.
//I use Form to create a new VO object in the create() method
}
}
How should the request be captured inside the update() function so that I can partially update my resource? I am not able to find good documentations to know about PATCH operations for Play! Framework.
Edit: I have seen about WSRequest for Patch operation, but I am not sure how to use that. Will that be helpful?
This is a sample code using ebeans in Play Framework
public Item patch(Long id, JsonNode json) {
//find the store item
Item item = Item.find.byId(id);
if(item == null) {
return null;
}
//convert json to update item
Item updateItem;
updateItem = Json.fromJson(json, Item.class);
if(updateItem.name != null){
item.name = updateItem.name;
}
if(updateItem.price != null){
item.price = updateItem.price;
}
item.save();
return item;
}
Related
I've came upon a problem and I am struggling for 5 hours.
I've created a Java API (right now just for testing) in Eclipse using Jersey and this is my first time creating an API.
I am using Postman to test it.
When calling the GET method to just return a string "Hello" it's working great.
The problem is when I try the POST method that accepts an object of a class Person as an input parameter and also just returns string "Hello" I get Internal Server Error. I know I am not using the Person object right now, but I am just testing the input parameter from Postman and it's not working.
I tried to change the function in the API to be without an input parameter and just to be POST and it works like that, it prints the "Hello", but I need that input parameter for later...
The problem is somewhere around the creation of the object in the xml code in Postman maybe, I don't know.
Any suggestion is welcomed.
Here is the code for the API with the methods get and post
#Path("/employee")
public class API {
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
#Path("/examplepost")
public String examplePost(Person p) {
return "Hello";
}
#GET
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
#Path("/exampleget")
public String exampleGet() {
return "Hello";
}
}
This is the Person class:
#XmlRootElement(name = "person")
public class Person {
private String name;
private int age;
private int id;
public Person(String name, int age, int id) {
super();
this.name = name;
this.age = age;
this.id = id;
}
public Person() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Override
public String toString(){
return id+"::"+name+"::"+age;
}
}
And these are the results from Postman
GET Method
POST Method
The problem is that the argument Person p does not have the right class type. the function is consuming an XML not an instance of Person. So you have to use JAXBElement that represents information about an Xml Element
public String examplePost(JAXBElement<Person> p) {
Person person = p.getValue();
return "Hello";
}
[OPTIONAL] Also in PostMan: you have to specify what data you are sending by adding the xml version at the beginning
<?xml version="1.0"?>
<Person>
<!-- Person's attributes -->
</Person>
I am using Spring's RestTemplate to convert a JSON response from the RiotAPI into my BasicSummoner object. I believe the issue is with converting the JSON response into my object. After calling getForObject() all of the object's fields are null/empty. Any help is appreciated as this is my first Spring project and first time using Riot's API.
I have verified that the JSON resonse is correct and looks like this:
{
"riotschmick": {
"id": 585897,
"name": "RiotSchmick",
"profileIconId": 782,
"summonerLevel": 30,
"revisionDate": 1469155559000
}
}
My request looks like this:
public BasicSummoner requestBasicSummoner() {
RestTemplate template = new RestTemplate();
String mes = "https://na.api.pvp.net/api/lol/na/v1.4/summoner/by-name/RiotSchmick?api_key=<my-api-key>";
BasicSummoner summoner = template.getForObject(mes, BasicSummoner.class);
log.info(summoner.toString());
return summoner;
}
And the object BasicSummoner looks like this:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown = true)
public class BasicSummoner {
private long id;
private String name;
private int profileIconId;
private long revisionDate;
private long summonerLevel;
public BasicSummoner() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getProfileIconId() {
return profileIconId;
}
public void setProfileIconId(int profileIconId) {
this.profileIconId = profileIconId;
}
public long getRevisionDate() {
return revisionDate;
}
public void setRevisionDate(long revisionDate) {
this.revisionDate = revisionDate;
}
public long getSummonerLevel() {
return summonerLevel;
}
public void setSummonerLevel(long summonerLevel) {
this.summonerLevel = summonerLevel;
}
#Override
public String toString() {
return "id=" + id + ", name=" + name + " , summoner level=" + summonerLevel;
}
}
Your JSON is not a single Object, but an Object inside another Object.
This means that to use your code as it is now, you need to unwrap the inner Object, or change the structure to something else.
The response seems to fit a Map<String, BasicSummoner>
I am using Retrofit and Parceler libraries in order to communicate with my server.
The server has the following two API methods:
GET /api/metadata/{id} that returns the following JSON
{
"id": 1,
"active": true,
"location": {
"type": "Point",
"coordinates": [
30.0000,
30.0000
]
}
}
POST /api/metadata/{id} that expects the following JSON
{
"id": 1,
"active": true,
"location_latitude": 30.0000,
"location_longitude": 30.0000
}
That is so for historic reasons and cannot change.
In my android application, I declare Retrofit in the following way:
public interface ServerApi {
#GET("/api/metadata/{id}")
Metadata getMetadata(#Path("id") int id);
#POST("/api/metadata/{id}")
Metadata updateMetadata(#Path("id") int id, #Body Metadata metadata);
}
Parcel classes are defined in the following way:
Metadata:
#Parcel
public class Metadata {
#SerializedName("id")
private Integer id;
#SerializedName("location")
private GeometryPoint location;
#SerializedName("location_latitude")
private float locationLatitude;
#SerializedName("location_longitude")
private float locationLongitude;
public void setId(Integer id) {
this.id = id;
}
public void setLocationLatitude(float locationLatitude) {
this.locationLatitude = locationLatitude;
}
public void setLocationLongitude(float locationLongitude) {
this.locationLongitude = locationLongitude;
}
public void setLocation(GeometryPoint location) {
this.location = location;
}
public Integer getId() {
return id;
}
public float getLocationLatitude() {
return locationLatitude;
}
public float getLocationLongitude() {
return locationLongitude;
}
public GeometryPoint getLocation() {
return location;
}
}
GeometryPoint:
#Parcel
public class GeometryPoint {
#SerializedName("type")
private String type;
#SerializedName("coordinates")
private float[] coordinates;
public void setType(String type) {
this.type = type;
}
public void setCoordinates(float[] coordinates) {
this.coordinates = coordinates;
}
public String getType() {
return type;
}
public float[] getCoordinates() {
return coordinates;
}
}
I would like to use Metadata class throughout my application. I would like to query the server, receive Metadata, update it, and send it to the server. Obviously, the formats of metadata differ between the GET and POST. As such, I'd like to have GET coverted to POST format upon receiving it.
My question is whether it is possible to somehow declare annotations so that Retrofit and Parceler would be aware of location parameter, deserialize it from JSON but write it to Metadata class via setLocation() method where I could break it down into `location_latitude' and 'location_longitude'.
This is some pseudocode of the desired Metadata class:
#Parcel
public class Metadata {
#SerializedName("id")
private Integer id;
// I'd like not having location variable defined at all
// but use some annotation magic :)) to tell GSON to deserialize
// JSON and call setLocation() when it tries to process location
// parameter of the server response
/*
#SerializedName("location")
private GeometryPoint location;
*/
#SerializedName("location_latitude")
private float locationLatitude;
#SerializedName("location_longitude")
private float locationLongitude;
public void setId(Integer id) {
this.id = id;
}
public void setLocationLatitude(float locationLatitude) {
this.locationLatitude = locationLatitude;
}
public void setLocationLongitude(float locationLongitude) {
this.locationLongitude = locationLongitude;
}
public void setLocation(GeometryPoint location) {
this.location_latitude = location.getCoordinates()[1];
this.location_longitude = location.getCoordinates()[0];
}
public Integer getId() {
return id;
}
public float getLocationLatitude() {
return locationLatitude;
}
public float getLocationLongitude() {
return locationLongitude;
}
// No need for getLocation method
}
Or am I just being silly (I literally picked up Retrofit, Parceler, and GSON awareness yesterday) and should create two metadata classes MetadataExternal and MetadataInternal to use for receiving and sending to the server?
Currently, I want to get list of selected checkbox from a table.
and I tried to have a sample code as below :
public class Student {
public List<String> listSubject;
public List<String> getListSubject() {
return listSubject;
}
public void setListSubject(List<String> listSubject) {
this.listSubject = listSubject;
}
private int id;
private String name;
private int age;
public boolean single;
public boolean isSingle() {
return single;
}
public void setSingle(boolean single) {
this.single = single;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
public Student(List<String> listSubject, int id, String name, int age,
boolean single) {
super();
this.listSubject = listSubject;
this.id = id;
this.name = name;
this.age = age;
this.single = single;
}
}
And the blow is controller
and StudentForm to add information
after selected checkbox from form, I want to display all result to a view :
But until now, I still can't controller adding selected value into a listofSubject which I created for a student.
THe blow is the link of sample code which I am implementing :
https://dl.dropboxusercontent.com/u/11576807/spring-mvc-example.zip
Besides, I want to use a tag instead of submit button to redirect to result page.
And the system only allow user to select two options, at that time, the remain checkbox will be disabled.
Can you please share with me your solution in this case ?
Please tell me know the way to do it with the sample above.
Thanks
You already found the answer. Check stu.getListSubject(). All the checked items will populated to List by Spring MVC.
Your controller should look like this.
#RequestMapping(value = "/student/add", method = RequestMethod.POST)
public String addStudent(Student stu, ModelMap model){
for (String s: stu.getListSubject()) {
//You can see values populated
System.out.println("string: " + s);
}
model.addAttribute("name",stu.getName());
model.addAttribute("age", stu.getAge());
model.addAttribute("single", stu.isSingle());
model.addAttribute("listSubject", stu.getListSubject());
return "studentView";
}
And you have error in your studentView.jsp file.
Instead of this
<c:forEach items="listSubject" var="subject">
<td>${subject}</td>
</c:forEach>
use this:
<c:forEach items="${listSubject}" var="subject">
<td>${subject}</td>
</c:forEach>
You missed ${} .
I am writing some test code to learn spring-data with MongoDB. I can successfully create two Documents: Person and ADocument, where ADocument contains a reference to Person.
#Document
public class Person {
#Id
private ObjectId id;
#Indexed
private String name;
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
...
#Document
public class ADocument {
#Id
private ObjectId id;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
private String title;
private String text;
#DBRef
private Person docperson;
public Person getDocperson() {
return docperson;
}
public void setDocperson(Person docperson) {
this.docperson = docperson;
}
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
}
The problem arises when I try to get all the 'adocuments' related to a person by using the person's ID (once the person's name is provided):
public List<ADocument> loadDocumentsByPersonName(String pname) {
Query qPerson = new Query().addCriteria(Criteria.where("name").is(pname));
qPerson.fields().include("_id");
Person pers = mongoTemplate.findOne(qPerson, Person.class);
ObjectId persId = pers.getId();
Query qDoc = new Query().addCriteria(Criteria.where("person.$id").is(persId));
System.out.println(qDoc.toString());
List<ADocument> list2 = mongoTemplate.find(qDoc, ADocument.class);
return list2;
}
Everyting works fine except that list2 is always empty (while it shouldn't).
System.out.println(qDoc.toString()) gives something like:
Query: { "person.$id" : { "$oid" : "536a0d50e4b0d0c10297f2ab"}}, Fields: null, Sort: null
If I try to issue the query above on the Mongo shell I get the following:
db.adocument.find({ "person.$id" : { "$oid" : "536a0805e4b0af174d0b5871"}})
error: {
"$err" : "Can't canonicalize query: BadValue unknown operator: $oid",
"code" : 17287
}
While if I type
db.adocument.find({ "person.$id" : ObjectId("536a0805e4b0af174d0b5871")})
I actually get a result!
I am using MongoDB 2.6.0 and Spring Data 1.4.2.
I really can't figure out what's going on... Any help is extremely appreciated!
I got it!
For some reason, I had to explicit the collection name in the Query:
List list2 = mongoTemplate.find(qDoc, ADocument.class, COLLECTION_NAME);
where COLLECTION_NAME="adocument".
As for the shell behaviour, it seems that Query.toString() does never return a correct syntax to be cut and paste for shell execution.