i'm trying to consume a Json response using RestTemplate in java with Jackson annotations, and i have a resource that have many name properties like so:
{
-name1:{
id:2,
price:12,
name:"Bob:
},
-name2:{
id:111,
price:1.1,
name:"Ron:
},
-name3:{
id:10,
price:33,
name:"jhon:
},
}
and the list go on like this.
this is my code of how to get one of the entities, like name1 object:
public class Class1 {
private RestTemplate restTemplate;
private String url = "https://url.com/api";
private Response response;
private Market market ;
public class1(){
restTemplate = new RestTemplate();
response = restTemplate.getForObject(url,Response.class);
}
#Override
public Market getResults() {
market = response.getResult();
System.out.println(Market);
return null;
}
}
and the response class is like so :
#JsonIgnoreProperties(ignoreUnknown = true)
#Getter
#Setter
#NoArgsConstructor
public class Response {
#JsonProperty("name1")
private Market result;
}
how can i get all those elements as an array or ArrayList?
this API is from 3rd party website and there's many entities liek that in the Json response.
thanks in advance.
So in the Json above, it is not an array but a list of key value pair.
This is what an array looks like in Json:
{
marketResults: [
{
id:2,
price:12,
name:"Bob:
},
{
id:111,
price:1.1,
name:"Ron:
},
{
id:10,
price:33,
name:"jhon:
}
]
}
Then what you could have done is:
public class Response {
private List<Market> marketResults;
}
But since your example is a map, you need to to use a MAP
public class Response {
private Map<String, Object > marketResults;
}
That post actually similar to yours: Reading JSON map structure via spring boot
If you can use Gson library that has native support for this use-case.
Keeps your code clean and typed.
#Getter
#Setter
public class Response {
#SerializedName(value = "name1", alternate={"name2","name3"})
private Market result;
}
#SerializedName is the #JsonProperty equivalent in Gson.
Related
I have a POST endpoint which accepts a JSON as request body.
#Path("/drink")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class DrinkResource {
#POST
public Drink getDrink(Fruit fruit) {
return new Drink(fruit.getName());
}
}
The request body is supposed to be deserialized into this POJO :
public class Fruit {
private final String name;
#JsonCreator
public Fruit(#JsonProperty("name") String name) {
this.name = name;
}
public String getName() {
return name;
}
}
I'm using Jackson for the deserialization.
Is it possible to make the deserialization fail when the JSON in the request body has duplicate keys ?
For example, if the request body looks like this : {"name" : "banana", "name" : "orange"}, I would like to get a 500 status code or another kind of error instead of having the json deserialized with the last property.
Basically, I'm looking for a solution with the same logic as the JsonParser.Feature.STRICT_DUPLICATE_DETECTION with the ObjectMapper but for a POST endpoint.
I'm also using quarkus so I don't know if there is a property for this. Something similar to quarkus.jackson.fail-on-unknown-properties=true but for the duplicate properties.
Add the following:
#Singleton
public class MyCustomizer implements ObjectMapperCustomizer {
#Override
public void customize(ObjectMapper objectMapper) {
objectMapper.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION);
}
}
If you do, the ObjectMapper that Quarkus uses will throw a JsonParseException thus leading to a HTTP 400 response.
I have a java service which calls an API and gets the following in return
{
"status": {
"service1": {
"available": true,
...
},
"service2": {
"available": false,
...
},
...
}
}
How can i use .readEntity (or something else) to get the object for a given field; say service2
Or just convert all objects inside status to a List<> of objects
String output = response.readEntity(String.class); works, but i dont know how to parse it properly to something useful as i dont want to search in the string
import javax.ws.rs.client.Client;
import javax.ws.rs.core.Response;
Response response = client
.target(url)
.request()
.get();
String output = response.readEntity(String.class);
You can model the data with a POJO and use that to extract the data. A model of the JSON that you've posted might look something like
public static class ResponseData {
private Map<String, Service> status;
// getters and setters
public static class Service {
private boolean available;
// getters and setters
}
}
The getters and setters should use JavaBean naming convention. Then make sure you have a JSON provider such as Jackson
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey2.version}</version>
</dependency>
Now you can simply pass the POJO class to the readEntity() method and it will deserialize the JSON into your POJO class.
ResponseData resData = res.readEntity(ResponseData.class);
Service service2 = resData.getStatus().get("service2");
For more details on modeling your JSON, you can do a Google search for "data binding with Jackson"
For some reason java can't map DTO with requestBody and all values are default ones, as for request it works, with payload for ex. "{"productId":1,"commitment":6,"returnMonths":"2"}"
DTO
#Data
public class Request {
private int productId;
private int commitment;
private String returnMonths;
// contructers
}
Controller :
#PostMapping(value = "/calculate", consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String calculatePrice(#RequestBody Request request) {
productService.calculatePrice(request);
return "Success";
}
front request:
submit: async function() {
let request = {
productId: this.productSelected,
commitment: this.optionSelected,
returnMonths: this.input
};
let data = await getCalculation(request);
console.log(data);
}
DTO maps as:
productId : 0
commitment : 0
returnMonths : null
Tried an exact copy of your code and it worked when tested with Postman. This makes me think it's either something to do with the FE or maybe some issue in the service. I'd check if the Frontend really sends the data.
Try to annotation Request class with #AllArgsConstructor like:
#AllArgsConstructor
#Data
public class Request {
private int productId;
private int commitment;
private String returnMonths;
}
If your request body contains properties that is date such as LocalDateTime, make sure to format it in your DTO using #JsonFormat(pattern="") respecting the input value.
I'm having a trouble with deserializing JSON like:
[{
"foo": "bar",
"example": "value"
}]
For some reason, API I use packs it in unnamed Array with one element. I'm accessing that API with RestController.
As long as I used default Jackson Converter, I could easily prepare model classes like that (getters, setters, constructors omitted for clarity):
public class Model {
private String foo;
private String example;
}
public class Response {
#JsonValue
private List<Model> model;
}
Repository class
public class FooRepository {
private RestTemplate restTemplate;
public String getFoo() {
ResponseEntity<Response> response =
restTemplate
.exchange("http://localhost:8080/api"
HttpMethod.GET,
new HttpEntity<>(new HttpHeaders()),
Response.class);
return response.getBody().getModel().get(0).getFoo();
}
I cannot find any annotation that works like #JsonValue for Jsonb. Is there one? Or maybe there is another way to fix my problem without using Jackson?
edit:
I figured out a workaround solution.
public String getFoo() {
ResponseEntity<List<Model>> response =
restTemplate
.exchange("http://localhost:8080/api"
HttpMethod.GET,
new HttpEntity<>(new HttpHeaders()),
ParameterizedTypeReference<List<Model>>());
return response.getBody().get(0).getFoo();
}
This question already has answers here:
#RequestBody and #ResponseBody annotations in Spring
(5 answers)
Closed 6 years ago.
I'm able to parse nested json string with ObjectMapper class. The problem here is it's getting difficult to parse the nested json as I had to write lot of boilerplate code to achieve the end result of parsing json to java objects. Is there any better way of parsing in spring other than this approach?
Domain classes :
Components class
public class Components {
private String name;
private String url;
private List<Properties> properties;
// getters and setters
}
properties class :
public class Properties {
private String name;
private String value;
private boolean exclusions;
// getters and setters
}
Controller class:
#RestController
public class TempController {
String jsonResponse = null;
#RequestMapping("/")
public #ResponseBody String getResponse(){
JsonreadApplication ja = new JsonreadApplication();
jsonResponse = ja.readJsonFile();
//System.out.println(jsonResponse);
return jsonResponse;
}
#RequestMapping("/temp")
public #ResponseBody String getTempByServerType(String jsonResponse){
jsonResponse = getResponse();
Components components = new Components();
ObjectMapper objectMapper = new ObjectMapper();
try {
Iterator<Entry<String, JsonNode>> fieldNames = objectMapper.readTree(jsonResponse).fields();
while(fieldNames.hasNext()){
System.out.println(fieldNames.next());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "I've parsed json!";
}
}
json file :
{
"name": "CatalogTools",
"url": "/atg/commerce/catalog/CatalogTools/",
"properties": [
{
"name": "queryASAFFabrics",
"value": "skuType=\"ASAF_FABRIC\" AND NOT basicColor IS NULL ORDER BY dynamicAttributes.fabricpriceband, basicColor, dynamicAttributes.fabrictype, dynamicAttributes.asafpattern, dynamicAttributes.asaffabricbrand",
"exclusions": "false"
},
{
"name": "loggingDebug",
"value": "true",
"exclusions": "false"
}
]
}
As said earlier, parsing can be done with ObjectMapper instance but with lot of boilerplate code and hardcoding json element names. Is there a better way of doing this? I'm using jackson library to parse the json file.
I've referred many links online where #RequestBody annotation was used for HTTP POST operations. Here, it is a HTTP GET request.
Any help is highly appreciated.
Many Thanks in advance.
You can use jsonchema2pojo.
Put there the json's example, set "Source Type" as JSON, select Annotation style to Jackson 2.x or 1.x and then click at "Zip". Voila, the whole model is generated!