I just started using java for a project, and I have this doubt. I'm trying to get a JSON answer with this code, but I don't know how to concatenate a string with a function in Java Spring. I have this controller, with this result:
public class BbController {
#Autowired
BbService bbService;
private static final String RESP_SUCCESS = "{\"result\" : { \"status\": true, \"http_code\" : 200, \"info\": \"list successfully obtained.\"}}";
#RequestMapping(value= "/all", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public String getAllContracts() {
return RESP_SUCCESS + ",{" + this.bbService.getAllContracts() + "}";
}
}
<---------- RESULT --------->>>
{
"result": {
"status": true,
"http_code": 200,
"info": "list successfully obtained."
}
},
{
[com.example.entity.BbEntity#3ddd5cfb, com.example.entity.BbEntity#1a57ff51
]
}
Without the concatenation and just returning return this.bbService.getAllContracts(), the output would be
[
{
"id": 12345,
"id_client": 123,
"n_contracts": 2,
"default_number": 2
},
{
"id": 1,
"id_client": 12,
"n_contracts": 2,
"default_number": 2
}
]
My service would be
public class BbService {
#Autowired
BbDao bbDao;
public List<BbEntity> getAllContracts(){
return this.bbDao.findAll();
}
}
Is there any way to get this result?
{
"result": {
"status": true,
"http_code": 200,
"info": "list successfully obtained."
}
},
{
[
{
"id": 12345,
"id_client": 123,
"n_contracts": 2,
"default_number": 2
},
{
"id": 1,
"id_client": 12,
"n_contracts": 2,
"default_number": 2
}
]
}
Thanks in advance
Looks like this.bbService.getAllContracts() returns a class that does not implement toString(), therefor it gets this ugly default print, you can implement it to get the better represenataion that you want.
But I think the better solution should not be to fix the toString, but rather to change the return value of your method from a string to some class you create. If you're using spring, it will be serialized into json once you do it
if this.bbService.getAllContracts() , getAllContracts() returns the class it self, it will be autamatically serialized into Json.
Related
In my application, we have a method that accepts JSON and path which tell us which object we need to get from that JSON. Buth both JSON and path are dynamic, I can't predict that every time we get a request we are getting the same JSON and path.
Example:
{
"company": {
"employees": {
"employee": {
"department": {
"departmentId": 1,
"departmentName": "Developer"
},
"employeeDetails": {
"id": 1,
"name": "abc"
}
}
}
}
}
and the path is company.employees.employee.department. And the requirement is when I get this path I only need that nested JSON object with employee details. Expected output:{
"company": {
"employees": {
"employee": {
"department": {
"departmentId": 1,
"departmentName": "Developer"
}
}
}
}
}
I am confused about your requirement. There is ambiguity in your question. I am thinking that you want to access employeeDetails from the JSON. Here is the solution for that:
var data = {
"company": {
"employees": {
"employee": {
"department": {
"departmentId": 1,
"departmentName": "Developer"
},
"employeeDetails": {
"id": 1,
"name": "abc"
}
}
}
}
}
var employee = data.company.employees.employee // this will store the nested json which is having employeeDetails
console.log(employee)// nested JSON with employeeDetails
console.log(employee.employeeDetails)// this will give you the employeeDetails
The method may look like:
const getData = (json, path) => {
let current = json;
const keys = path.split('.');
for (let key of keys) {
if (!current) {
break;
}
current = current[key];
}
return current;
};
getData(yourJSON, 'key1.key2.key3');
I have a List returned as response. I need to get one item from list using product.name and tariffPlan.name.
[
{
"id": 123,
"product": {
"id": 1,
"code": "credit",
"name": "Credit"
},
"tariffPlan": {
"id": 1,
"code": "gold",
"name": "Gold"
}
},
{
"id": 234,
"product": {
"id": 2,
"code": "debit",
"name": "Debit"
},
"tariffPlan": {
"id": 1,
"code": "gold",
"name": "Gold"
}
}
]
I use Java8. Here is my method. I got List of Card.class elements. And then I need to get single Item from list with specified "product.name" and "tariffPlan.name".
public List<Card> getCardId(String productName, String tariffPlanName) {
return given()
.param("product.name", productName)
.param("tariffPlan.name", tariffPlanName)
.when().get("/").then()
.extract().jsonPath().getList("", Card.class);
}
Is it possible to do it with restAssured? Maybe use .param method like in my example? But in my example .param method is ignored. Thank you for your ideas.
UPD. My decision is:
public Card getCard(String productName, String tariffPlanName) {
List<Card> cardList = given()
.when().get("/").then()
.extract().jsonPath().getList("", Card.class);
return cardList.stream()
.filter(card -> card.product.name.equals(productName))
.filter(card -> card.tariffPlan.name.equals(tariffPlanName))
.findFirst()
.get();
}
If you need to get a value from response json list, here's what worked for me:
Json sample:
[
{
"first": "one",
"second": "two",
"third": "three"
}
]
Code:
String first =
given
.contentType(ContentType.JSON)
.when()
.get("url")
.then()
.extract().response().body().path("[0].first")
Actually, you can but... you need to handle deserialization issue of default mapper becase if you try do the following:
.extract().jsonPath().getList("findAll {it.productName == " + productName + "}", Card.class);
You will failing on converting HashMap to your object type. It happens because of using gpath expression in path provides json without double quotes on keys by default. So you need to prettify it with (you can put it in RestAssured defaults):
.extract().jsonPath().using((GsonObjectMapperFactory) (aClass, s) -> new GsonBuilder().setPrettyPrinting().create())
And as result your would be able to cast things like that:
.getObject("findAll {it.productName == 'productName'}.find {it.tariffPlanName.contains('tariffPlanName')}", Card.class)
See full example:
import com.google.gson.GsonBuilder;
import io.restassured.http.ContentType;
import io.restassured.mapper.factory.GsonObjectMapperFactory;
import lombok.Data;
import org.testng.annotations.Test;
import java.util.HashMap;
import java.util.List;
import static io.restassured.RestAssured.given;
public class TestLogging {
#Test
public void apiTest(){
List<Item> list = given()
.contentType(ContentType.JSON)
.when()
.get("https://jsonplaceholder.typicode.com/posts")
.then().log().all()
.extract().jsonPath().using((GsonObjectMapperFactory) (aClass, s) -> new GsonBuilder().setPrettyPrinting().create())
.getList("findAll {it.userId == 6}.findAll {it.title.contains('sit')}", Item.class);
list.forEach(System.out::println);
}
#Data
class Item {
private String userId;
private String id;
private String title;
private String body;
}
}
Here a kotlin example:
#Test
fun `creat endpoint with invalid payload should return 400 error`() {
val responseError: List<ErrorClass> = Given {
spec(requestSpecification)
body(invalidPayload)
} When {
post("/endpoint")
} Then {
statusCode(HttpStatus.SC_BAD_REQUEST)
} Extract {
body().`as`(object : TypeRef<List<ErrorClass>>() {})
}
responseError shouldHaveSize 1
responseError[0].field shouldBe "xxxx"
responseError[0].message shouldBe "xxx"
}
Suppose you want to fetch the value of the id, when product name is "Credit" and tariffPlan is "Gold".
Use
from(get(url).asString()).getList("findAll { it.product.name == 'Credit' && it.tariffPlan.name == 'Gold'}.id");
Where url - http/https request and get(url).asString() will return a JSON response as string.
I have a REST API call that returns the following JSON object. I need to parse this with Spring's RestTemplate. The problem is that the first key ISBN:0132856204 is variable (the numbers change depending on the book). How would I go about doing this?
{
"ISBN:0132856204": {
"publishers": [
{
"name": "Pearson"
}
],
"pagination": "xxiv, 862p",
"identifiers": {
"isbn_13": [
"978-0-13-285620-1"
],
"openlibrary": [
"OL25617855M"
]
},
"weight": "1340 grams",
"title": "Computer networking",
"url": "https://openlibrary.org/books/OL25617855M/Computer_networking",
"number_of_pages": 862,
"cover": {
"small": "https://covers.openlibrary.org/b/id/7290810-S.jpg",
"large": "https://covers.openlibrary.org/b/id/7290810-L.jpg",
"medium": "https://covers.openlibrary.org/b/id/7290810-M.jpg"
},
"publish_date": "2013",
"key": "/books/OL25617855M",
"authors": [
{
"url": "https://openlibrary.org/authors/OL31244A/James_F._Kurose",
"name": "James F. Kurose"
},
{
"url": "https://openlibrary.org/authors/OL658909A/Keith_W._Ross",
"name": "Keith W. Ross"
}
],
"subtitle": "A Top-Down Approach"
}
}
In here "ISBN:0132856204" is a value and also a key for your business.
To get ISBN first, what about wrapping json content with 1 more closure?
{
"yourAwesomePlaceHolderKey" :
{
"ISBN:0132856204": {
......
}
}
}
First get the ISBN key as a value, then your ISBN value can be used as a key to get related content.
First goal will be extracting -String1,Object1- pair where String1 is "yourAwesomePlaceholderKey" and second goal will be again extracting -String2,Object2- from Object1 where String2 is your ISBN key.
This is the way I solved it, using JsonPath for getting the book out of the JSON object and Jackson for mapping it to a Book object:
RestTemplate restTemplate = new RestTemplate();
String isbn = "0132856204";
String endpoint = "https://openlibrary.org/api/books?jscmd=data&format=json&bibkeys=ISBN:{isbn}";
//Get JSON as String
String jsonString = restTemplate.getForObject(endpoint, String.class, isbn);
//Configure JsonPath to use Jackson for mapping
Configuration.setDefaults(new Configuration.Defaults() {
private final JsonProvider jsonProvider = new JacksonJsonProvider();
private final MappingProvider mappingProvider = new JacksonMappingProvider();
#Override
public JsonProvider jsonProvider() {
return jsonProvider;
}
#Override
public MappingProvider mappingProvider() {
return mappingProvider;
}
#Override
public Set<Option> options() {
return EnumSet.noneOf(Option.class);
}
});
//Parse the JSON as a book
Book book = JsonPath.parse(jsonString).read("$.ISBN:" + isbn, Book.class);
You can use JsonProperty to solve
#JsonProperty("ISBN:0132856204")
I have several controllers that are automatically creating REST endpoints.
#RepositoryRestResource(collectionResourceRel = "books", path = "books")
public interface BooksRepository extends CrudRepository<Books, Integer> {
public Page<Books> findTopByNameOrderByFilenameDesc(String name);
}
When I visit: http://localhost:8080/Books
I get back:
{
"_embedded": {
"Books": [{
"id": ,
"filename": "Test123",
"name": "test123",
"_links": {
"self": {
"href": "http://localhost:8080/books/123"
},
"Books": {
"href": "http://localhost:8080/books/123"
}
}
}]
},
"_links": {
"self": {
"href": "http://localhost:8080/books"
},
"profile": {
"href": "http://localhost:8080/profile/books"
},
"search": {
"href": "http://localhost:8080/books/search"
},
"page": {
"size": 20,
"totalElements": 81,
"totalPages": 5,
"number": 0
}
}
}
When I create my own controller:
#Controller
#RequestMapping(value = "/CustomBooks")
public class CustomBooksController {
#Autowired
public CustomBookService customBookService;
#RequestMapping("/search")
#ResponseBody
public Page<Book> search(#RequestParam(value = "q", required = false) String query,
#PageableDefault(page = 0, size = 20) Pageable pageable) {
return customBookService.findAll();
}
}
I'll get a response back that looks nothing like the automatically generated controller response:
{
"content": [{
"filename": "Test123",
"name" : "test123"
}],
"totalPages": 5,
"totalElements": 81,
"size": 20,
"number": 0,
}
What do I need to do to make my response look like the automatically generated response? I want to keep it consistent, so I don't have to rewrite code for a different response. Should I be doing it a different way?
Edit: Found this: Enable HAL serialization in Spring Boot for custom controller method
But I don't understand what I need to change in my REST Controller to enable: PersistentEntityResourceAssembler. I've searched on Google for PersistentEntityResourceAssembler, but it keeps leading me back to similar pages without much of an example (or the example doesn't seem to work for me).
As #chrylis suggested you should replace your #Controller annotation with #RepositoryRestController for spring-data-rest to invoke it's ResourceProcessors for customizing the given resource.
For you resource to follow the HATEOAS specification (like your spring-data-rest BooksRepository) your method declaration return type should be like HttpEntity<PagedResources<Resource<Books>>>
For converting your Page object to PagedResources:
You need to autowire this object.
#Autowired
private PagedResourcesAssembler<Books> bookAssembler;
Your return statement should be like
return new ResponseEntity<>(bookAssembler.toResource(customBookService.findAll()), HttpStatus.OK);
These changes should help you to get a org.springframework.hateoas.Resources compliant response containing the "_embedded" and "_links" attribute.
First post, so sorry if my terminology is unclear.
So I'm using retrofit to GET an array of objects from a link: BASE_URL/todos?userId=1.
When using:
#GET("todos?userId={userId}")
Call<List<ToDoObject>> listTodos(#Query("userId") int userId);
for this retrofit call:
retrofit.Retrofit retrofit = new retrofit.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
BaseServiceAPI service = retrofit.create(BaseServiceAPI.class);
Call<List<ToDoObject>> listToDos = service.listTodos(getUserId());
listToDos.enqueue(new Callback<List<ToDoObject>>() {
#Override
public void onResponse(Response<List<ToDoObject>> response, retrofit.Retrofit retrofit) {
if (response.body() != null) {
for (int i = 0; i < response.body().size(); i++) {
toDoObjectList.add(response.body().get(i));
}
toDoRecycAdapter.notifyDataSetChanged();
}else Log.d("flow", "no go");
}
#Override
public void onFailure(Throwable t) {
Log.d("flow", "todo failure: " + t.getMessage());
}
});
to get this array:
[
{
"completed": false,
"dueDate": "2016-07-31 06:38:00",
"id": 1,
"title": "Find your son Duke",
"userId": 1
},
{
"completed": false,
"dueDate": "2017-05-24 07:30:00",
"id": 4,
"title": "Rule the City ",
"userId": 1
},
{
"completed": true,
"dueDate": "2016-1-30 1:15:00",
"id": 6,
"title": "Run The ",
"userId": 1
},
{
"completed": true,
"dueDate": "2016-1-30 1:28:00",
"id": 7,
"title": "Hmmm",
"userId": 1
}
]
I receive this error: "FATAL EXCEPTION: Caused by: java.lang.IllegalArgumentException: URL query string "userId={userId}" must not have replace block. For dynamic query parameters use #Query."
I thought this was the proper case to use Query. I even tried Path but still nothing. I don't know many other programmers to ask and I've been at this for weeks. Can someone tell me what I'm doing wrong please??
Thanks in advance
You should just leave #Query parameter in your request and remove it from #GET. Also you say that you need to request only specific object, so why your response contains the list Call<List<ToDoObject>>?
#GET("todos")
Call<ToDoObject> specificTodo(#Query("userId") int userId);