I'm using Jersey with javax.ws.rs.*.
I have the following function:
#PUT
public R modify(#PathParam("id") int id, R in) throws Exception {
...
}
Where R (Resource) is a JSON object containing the fields to be updated. Example:
{
"name": "Building",
"address": "Street number",
"address2": "Lower floor"
}
The update process is working, but as an extra measure for our website developers which are using this API I want the method to throw an exception when a field is sent which isn't allowed. Example:
{
"name": "Qcast HQ",
"illegalField" : "someValue"
}
Because the input gets treated as a Resource object I am not sure whether the method can check for illegal fields. Maybe there is an annotation or something I can use to check for this?
Related
I was trying to call data from Directus API, and return certain data in JSON Format on my localhost.
I'm working on a project where we need to build an API layer.
My code:
public String findAllPersons(HttpServletResponse response, String showFields) throws IOException{
try {
// Call to the database (this part is normally not a problem
String url = "https://cuxhsjf3.directus.app/items/blog";
PersonRoot personRoot = template.getForObject(url, PersonRoot.class);
// I used the Objectmapper, since I'm going from normal data to Json.
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode personsObject = objectMapper.createObjectNode();
ArrayNode persons = objectMapper.createArrayNode();
// The array is going to loop through all the data objects from the database
for (int i = 0; i < personRoot.data.toArray().length; i++) {
// I put person objects into an array, and I return this array.
personsObject.put("id", personRoot.data.get(i).id);
personsObject.put("firstName", personRoot.data.get(i).firstName);
persons.add(personsObject);
System.out.println(persons);
}
String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(persons);
return json;
}
// The 'catch' was omitted as not related to the issue
Expected result:
{
"id": "050469ed-0501-4506-9951-794b41bf7e7f",
"firstName": "Elias"
},
{
"id": "0bfb52f7-3656-4202-8c24-2b63eaeca6a9",
"firstName": "Mathias"
},
{
"id": "3145fb95-afd7-4bc4-a62e-a8622b301db2",
"firstName": "Brent"
},
{
"id": "5b93c9b1-4bd1-4aa5-a5ca-d46e849cc58f",
"firstName": "Jef "
},
{
"id": "cb3d5d02-6b87-4aa1-b817-17550b3cf03c",
"firstName": "Jan"
}
}
The output I'm getting:
{
"id": "cb3d5d02-6b87-4aa1-b817-17550b3cf03c",
"firstName": "Jan"
},
{
"id": "cb3d5d02-6b87-4aa1-b817-17550b3cf03c",
"firstName": "Jan"
},
{
"id": "cb3d5d02-6b87-4aa1-b817-17550b3cf03c",
"firstName": "Jan"
},
{
"id": "cb3d5d02-6b87-4aa1-b817-17550b3cf03c",
"firstName": "Jan"
},
{
"id": "cb3d5d02-6b87-4aa1-b817-17550b3cf03c",
"firstName": "Jan"
}
}
As #markspace has pointed out in the comments, you're repeatedly adding the reference to the same ObjectNode into the same JsonArray. When you're modifying this single object, all the elements in the get updated, and what you're getting is data of the very last person replicated multiple time.
You need to move creation of the ObjectNode into the loop, so that a new node would be instantiated for each person:
for (int i = 0; i < personRoot.data.toArray().length; i++) {
ObjectNode personsObject = objectMapper.createObjectNode(); // move object-creation inside the loop
personsObject.put(...);
personsObject.put(...);
persons.add(personsObject);
}
Sidenotes
Encapsulation in your custom class PersonRoot is broken. In Java, we use access modifier like protected and private to hide and protect the data within a class instance. And class is supposed to provide behavior which allow interacting with the state of its instances instead of exposing the instance variables directly (getters are the simplest example of such behavior). This might look alien to you, if previously you were working with languages like JavaScript, but Encapsulation is one of the crucial parts of Object-oriented programming, and it's a common practice in Java to design classes in such a way so that their data is properly encapsulated.
You might also want to learn about one of the GRASP design-patterns, namely The information expert pattern. Which states that an object containing the data should be provided with behavior that facilitates interaction with the data (it's not the responsibility of the code that make use of this object).
I.e. instead of doing this: personRoot.data.toArray().length you can introduce length() method in the PersonRoot class. And instead of writing personRoot.data.get(i) there could be a method like PersonRoot.getPerson(int index).
You can also make PersonRoot implement Iterable interface, so that it could be used as a source of data within an enhanced for-loop.
Given this simple class representing a Person object:
#Builder
public class Person {
int age;
String name;
}
Using builder to create an instance of Person and posting it to a server using Rest Assured:
public class main {
public static void main(String [] args){
Person p1 = Person.builder().name("John").build();
Person p2 = Person.builder().age(29).build();
//CODE TO SEND POST REQUEST USING REST ASSURED
}
}
As I understand it, the corresponding json string being received by the server would look something like this:
p1: { "name": "John", "age": 0 }
p2: { "name": null, "age": 29 }
My question is: How can I intentionally leave out certain attributes of a class?
Lets say that the endpoint which I am posting this data to requires that the schema contains an age or name attribute, but for testing purposes, I want to leave out the age attribute so that what is received looks something like this:
p1: { "name": "John" }
p2: { "age": 29 }
I am thinking I could create a separate, similar class representing a person, but removing the attribute in question. This just seems wrong and inefficient. Is there a better way? Or is there some sort of stuff going on under the hood that by intentionally uninitializing an attribute, its left out in the post request? Thanks for your time.
It depends on how you are sending the Person class, but you could opt to filter out the null values by using #JsonInclude(Include.NON_NULL), or have a different mapper configured depending on your use case.
This answer describes a way how to do this with either Jackson or Gson.
I have 2 fields that are being passed in request body. I have a conditional validation for if value1="OK" then value2 must be "PASSED" or raise a validationerror. It's working fine for single instance but in my json body I can pass multiple registration requests at once, like the Json request body shown below.
[
{
"Value1": "OK",
"Value2": "NOTPASSED"
},
{
"Value1": "OK",
"Value2": "PASSED"
}
]
How can I process second record, though validation for first record failed?
EDIT: Looking at the comments I thought to simplify the question even more:
these are the fields from request body:
#NotEmpty
private String value1;
#NotEmpty
private String value2;
I can pass multiple registrations in my json body as shown above.
let's say I passed a body like this:
[
{
"Value1": "",
"Value2": "NOTPASSED"
},
{
"Value1": "OK",
"Value2": "PASSED"
}
]
Since we have NotEmpty validation for value1, it will throw a validation error but second record with value1 as "OK" and value2 as "PASSED" doesn't have any validation errors. So I need a functionality to be able to process the second record while throwing error for the first record.
take that list of object and loop then after make a if else condition
and validate ..
list.forEach(object->{
if(object.getValue1()=="OK")
if(object.getValue2()=="PASSED")
//do ur logic because if pointer come here means it's valid
else
//do ur logic if not valid like raise a validation-error
//my suggestion is store invalid instances here and through in single
//shot
});
Request body is an array having multiple instances of
{
"Value1": "OK",
"Value2": "NOTPASSED"
}
You can loop through the array and validate each instance something like
List<VallidationError> validationErrorList = new ArrayList<>();
for(RequestElement element: requestBody) {// RequestElement is the class for each instance in the request body
if(element.getValue1().equals("OK") && !element.geValue2().equals("PASSED")) {
validationErrorList.add(new ValidationError());// Add validation error to a list of validation errors
} else {
processElement(element);// Process the record here
}
}
As you said it can be Empty String sometimes then instead of using #NotEmpty you should use #NotNull.
I guess it should solve your problem as on an empty string for value1 it will not throw validation.
Problem: I have a request body where I have a predefined POJO class, inside this class I need to add another object as parameter. This new object at a given time may have random properties/attributes/params. How can I achieve this?
{
"id": "{{id}}",
"enableTouchId": true,
"idleLogoutMinutes": 10,
"platformSpecificPreferences": {
"ios": {
"terms": "1234",
"privacy": "12345"
},
"web": {
"terms" : "abc"
},
"android": {
"newProperty" : "newValue"
}
}
}
So the new object I am trying to add is platformSpecificPreferences, which when hit using rest calls might or might not have all the properties shown here, which is why I cannot use redefined POJO class for platformSpecificPreferences and create its object.
Solution I tried:
I thought of using JsonObject inside request body, which makes
#JsonProperty("platformSpecificPreferences")
private JsonObject platformSpecificPreferences;
but the problem is, I am not able to hit the api as it doesnt accept this parameter and gives 404.
Thanks in advance.
You can use, kind must a predefined pojo for platformSpecificPreferences but in the pojo you need to ignore values that are not given in the rest call!
You can do this with a json annotation:#JsonIgnoreProperties(ignoreUnknown = true) in the Pojo above the class.
I am trying to loop over the array of objects in java. I'm posting this value from client side to server side which is java.
"userList": [{
"id": "id1",
"name": "name1"
},
{
"id": "id2",
"name": "name2"
}]
Now I want to get the value of each id and name. I tried the code below:
for (Object temp : userList)
System.out.print(temp);
System.out.print(temp.getId());
}
But the output I get is:[object Object]
I'm sorry for this stupid question. But how will I get the value of id and name?
You're getting [object Object] because you didn't turn your JavaScript object into JSON on the client side before sending it to your server--you need to use something like JSON.stringify(object) in the browser.
Next, you will need to unpack your JSON into some sort of Java structure. The preferable way to do this is to let an existing tool such as Jackson or Gson map it onto a Java object that looks like:
class User {
String id;
String name;
}
How to do this will depend on your framework, but Spring MVC (for example) supports it mostly automatically.
Implement the toString method for your class according to how you want the printed output to look.
For example...
public class User {
private String id;
private String name;
// Constructors, field accessors/mutators, etc...
#Override
public String toString() {
return String.format("User {id: %s, name: %s}", this.id, this.name);
}
}
Your question does not have complete information. You certainly are skipping steps.
Before you start using the object in java you need to cast the object.
ArrayList<User> convertedUserList = (ArrayList<User>)userList;
for (User temp : convertedUserList)
System.out.print(temp);
System.out.print(temp.getId());
}