How to get different parameters for different APIs like mentioned below - java

I need to get different parameters in response for 2 different APIs getAlland getbyID.
Now I get the same result -second json for both APIs.
I want to get 1st json in reponse of api getALL without the one-to-many relation and
I want to get 2nd json in reponse of api getbyid with one-to-many relation
First JSON Response:
{
"id":2,
"itemName":"book",
}
Second JSON Response:
{
"id":2,
"itemName":"book",
"owner":
{
"id":1,
"name":"John"
}
}
User Class
public class User {
public int id;
public String name;
#JsonBackReference
public List<Item> userItems;
}
Item class
public class Item {
public int id;
public String itemName;
#JsonManagedReference
public User owner;
}
can anyone help in this?

My suggestion is to make item class just for data transfer like:
public class ItemDTO {
public int id;
public String itemName;
}
Then in your controller you can do something like that:
#GetMapping('/get-all')
public ResponseEntity<ItemDTO> getAll() {
//get Item object
Item item = //e.g database call
ItemDTO itemDTO = new ItemDTO(item.id, item.name);
return ResponseEntity.ok(itemDTO);
}
#GetMapping('/get-by-id/{id}')
public ResponseEntity<Item> getAll(#PathVariable Integer id) {
Item item = //e.g get item by id
return ResponseEntity.ok(item);
}

Related

How to change JSON representation for single value java object?

I had a class like:
public class EmailAddress {
public String value;
public String tld() {...}
public String host() {...}
public String mailbox() {...}
}
Now I use this class in an Object / Entity:
#Entity
public class Customer {
public String name;
public EmailAddress mail;
}
Now, when I do a rest service for Customer, I get this format:
{
"id": 1,
"name": "Test",
"email": {
"value": "test#test.de"
}
}
But I only want "email": "test#test.de"
{
"id": 1,
"name": "Test",
"email": "test#test.de"
}
What I must do? I use Spring Boot and Hibernate Entities.
Thank you for any support
You should use DTO class in request handling and make mappings from DTO to Entity and backwards, e.g.:
public class CustomerDTO {
private Integer id;
private String name;
private String email;
}
You should use DataTransferObjects for your (REST) APIs.
The DTOs only contain the fields the interface should provide (or receive).
When receiving objects from the client and before returning the object from your Controller you can convert the DTOs to your domain model (Which could be your JPA entites classes).
Example for a controller method. We assume you get an object from an user-editor which contains all data you want to update in your database-objects and return the updated company DTO:
#PutMapping
public CustomerDto updateCustomer(CustomerEditorDto updatedCustomerDto) {
Customer updatedCustomer = CustomerConverter.convert(updatedCustomerDto);
updatedCustomer = customerService.updateCustomer(updatedCustomer);
return CustomerConverter.convert(updatedCustomer);
}
and your Converter class:
#NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CustomerConverter {
public static CustomerDto convert(Customer customer) {
CustomerDto result = null;
if (customer != null) {
// TODO: set fields in result-dto
}
return result;
}
public static Customer convert(CustomerEditorDto customer) {
Customer result = null;
if (customer != null) {
// TODO set fields in result;
}
return result;
}
}
and here are the DTOs
#Getter
#Setter
public class CustomerDto {
private Integer id;
private String name;
private String email;
}
#Getter
#Setter
public class CustomerEditorDto {
private Integer id;
private String firstName;
private String lastName;
private String email;
private String otherPropertyOrStuff;
}
This way you can separate the API modell from your JPA entites. You can use the same models for input/output. And you can even use a different model to work with inside your services and the finally convert them into your JPA entites, before persisting the data (or after reading the data).
There are tools which can take care of the conversion, like mapstruct.
* The above annotations #Getter, #Setter, ... are from project lombok and very are handy to generate boiler-plate code automatically.
I found an other easier solution, use a JsonSerializer on the entity Property:
#JsonSerialize(using = EmailAddressSerializer.class)
private EmailAddress email;
The serializer class:
public class EmailAddressSerializer extends StdSerializer<EmailAddress> {
public EmailAddressSerializer() {
super(EmailAddress.class);
}
protected EmailAddressSerializer(Class<EmailAddress> t) {
super(t);
}
#Override
public void serialize(EmailAddress email,
JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(email.value);
}
}

How to get an arrayJSON in webService Rest?

I am developing a WEb Service Rest in java, netbeans.
This is the JSON I want to receive:
{
"ticket":"2132158645161654561651616",
"avaliacoes":[
{
"id":1,
"nome":"Atendimento",
"nota":5,
"observacoes":"testeTEste"
},
{
"id":2,
"nome":"Atendimento",
"nota":5,
"observacoes":"testeTEste"
}
]
}
Reception Class
#PUT
#Consumes(MediaType.APPLICATION_JSON)
#Path("venda/enviardados")
public String postVenda(#QueryParam("key") String key, #QueryParam("serial") String serial, VendaAvaliacao va) {
...
}
Entity Classes
public class VendaAvaliacao {
private int id;
private String ticket;
//private List<VendaAvaliacaoInner> avaliacoes = new ArrayList<>(); //I've tried it too
private VendaAvaliacaoInner[] teste;
}
public class VendaAvaliacaoInner {
private int id;
private String nome;
private int nota;
private String observacao;
}
The ticket is received and populated, but array = null.
I've read other similar topics but they did not help .... how can I do?
https://pt.stackoverflow.com/questions/6046/convers%C3%A3o-de-string-json-para-objeto-java?rq=1
https://pt.stackoverflow.com/questions/159725/receber-valor-de-array-json-para-string-java
https://pt.stackoverflow.com/questions/290759/como-obter-objetos-de-um-array-de-json-usando-jsonarray-no-java
Maybe the service REST you are consuming don't populate the data in the array.
As an advice i would work with List<T> rather than T[]
Also i see that you are mapping 3 properties in you object but the id property don't exist in raw JSON isn't it ?

Subclass as json parent object

I have a requirement where I need a subclass as object while creating a json payload.
EventBase
public class EventBase {
#JsonProperty("event_id")
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
PaymentCapture (the sub class)
#JsonTypeName("resource")
public class PaymentCapture extends EventBase {
#JsonProperty("parent_payment")
private String parentPayment;
public String getParentPayment() {
return parentPayment;
}
public void setParentPayment(String parentPayment) {
this.parentPayment = parentPayment;
}
}
And I need a json payload in below form:
{
"id": "someId",
"resource": {
"parent_payment": "23434"
}
}
I can understand this violates inheritance relationship, but just want to know if there is any solution available or not.
The closest I could get when having similar problem was creating an adapter class. This solution prints one extra property which might be possible to be ignored if for example some inheritance was allowed but I assume that not and use just the declared classes in addition to the adapter, which is like:
#RequiredArgsConstructor
public class PaymentCaptureAdapterClass {
#NonNull
#JsonProperty
private PaymentCapture resource;
#JsonProperty
private String getId() {
return resource.getId();
}
}
using this with code:
ObjectMapper om = new ObjectMapper();
om.enable(SerializationFeature.INDENT_OUTPUT);
PaymentCapture pc = new PaymentCapture();
pc.setId("someId");
pc.setParentPayment("23434");
log.info("\n{}", om.writeValueAsString(new AdapterClass(pc)));
prints something like:
{
"resource" : {
"event_id" : "someId", // might be able to be ignored
"parent_payment" : "23434"
},
"id" : "someId"
}

Create a Post method to save an object with multiples id

I have in my controller:
#RestController
public class OneTwoController {
private OnTwoService _service;
//... more code
#PostMapping("/api/one-two")
#CrossOrigin
public ResponseEntity<ServiceResponse> save(#RequestBody OneTwo model) {
return ResponseEntity.ok().body( _service.Save(model));
}
In my entity:
#Entity(name = "OneTwo")
#Where (clause = "deleted='false'")
public class OneTwo{
#EmbeddedId
private OneTwoKey_id;
public OneTwo(OneTwoKey id) {
this._id = id;
}
#JsonProperty("oneTwo")
public void setId(OneTwoKey value) {
this._id = value;
}
The OneTwoKey class:
public class OneTwoKey implements Serializable {
#Column(name = "OneID")
private int _oneID;
#Column(name = "TwoID")
private int _twoID;
public OneTwoKey(int oneID, int twoID) {
this._oneID = oneID;
this._twoID = twoID;
}
}
The json that I send to the Rest API:
{
"oneTwo": {
"oneID": 83,
"twoID": 69
},
"deleted": true
}
The issue is that both ids arrive null, so the service can't do the insert on the DB.
How can I deal with those cases when the ids are more than one?
Try adding setters in the OneTwoKey class to make it easier for the JSON deserializer:
#JsonProperty("oneID")
public void setOneID(int oneID) {
this._oneID = oneID;
}
#JsonProperty("twoID")
public void setTwoID(int twoID) {
this._twoID = twoID;
}
Another solution is to create a DTO, use it to receive the data in the controller and then convert it to your entity:
public class OneTwoDTO {
private Map<String, Int> oneTwo;
private boolean deleted;
// setters & getters
}
Simply what you can do is instead of using
public ResponseEntity<ServiceResponse> save(#RequestBody OneTwo model) {
you can use
public ResponseEntity<ServiceResponse> save(#RequestBody String model) {
Now convert the String to json and get all the key value pairs, it would be easier if you have dynamic number of variables and you want to capture them all.
or you can use tools like jsonschema2pojo whick take a json schema and generate a pojo. In the json schema if you set
"additionalProperties": true
you can capture all the values.
Could you make sure the problem is not because of case sensitivity?
Lower case the column names. Also could you use public access on those variables as well? These are my initial guesses as to why the payload is not being binded correctly.
public class OneTwoKey implements Serializable {
#Column(name = "oneID")
public int _oneID;
#Column(name = "twoID")
public int _twoID;

Retrofit 2.0, Jackson and polymorphism: Could not find creator property with name <...>

I'm currently consuming a REST API with RetroFit & Jackson. Consider the following response JSON when retrieving users based on a search query when:
One result was found
{
name: "API name",
results: {
count: 1
users: {
username: "username",
age: 15
}
}
}
Multiple results were found
{
name: "API name",
results: {
count: 2,
users: [{
username: "username1",
age: 18
}, {
username: "username2",
age: 19
}]
}
}
As you can see, the users-property contains dynamic JSON: based on the found results, the value of "users" could either be a list of user objects, or 1 user object.
As such, I've designed my Java POJOs using polymorphism, as follows:
public class UserResponse {
#JsonProperty("name")
private String apiName
#JsonProperty("results")
private AResultList resultList
//Getters & Setters
//Constructor
#JsonCreator
public UserResponse(#JsonProperty("name") String name, #JsonProperty("results") AResultList r) {
this.apiName = name;
this.resultList = r;
}
}
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "classType"
)
#JsonSubTypes({
#JsonSubTypes.Type(value = ResultObject.class, name="ResultObject"),
#JsonSubTypes.Type(value = ResultList.class, name="ResultList")
})
public abstract class AResultList {
#JsonProperty("count)
private long totalCount;
//Getters & Setters
//Constructors
#JsonCreator
public AResultList(#JsonProperty("count) long count) {
this.totalCount = count;
}
}
public class ResultObject extends AResultList {
#JsonProperty("users")
private User user;
//Getters & Setters
//Constructor
#JsonCreator
public ResultObject(#JsonProperty("count) long count, #JsonProperty("users") User u) {
super(count);
this.user = u;
}
}
public class ResultList extends AResultList {
#JsonProperty("users")
private List<User> users;
//Getters & Setters
//Constructor
#JsonCreator
public ResultObject(#JsonProperty("count) long count, #JsonProperty("users") List<User> u) {
super(count);
this.users = u;
}
}
public class User {
#JsonProperty("username")
private String username;
#JsonProperty("age")
private long userAge;
//Getters & Setters
//Constructor
#JsonCreator
public User(#JsonProperty("username") String u, #JsonProperty("age") long a) {
this.userAge = a;
this.username = u;
}
}
For your information: A snippet for instantiating RetroFit
ObjectMapper o = new ObjectMapper();
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(JacksonConverterFactory.create(o))
.client(okClient)
.build();
Trying to retrieve this information, however, results in the following error:
com.fasterxml.jackson.databind.JsonMappingException: Could not find creator property with name 'count' (in class org.namespace.AResultList)
at [Source: java.io.InputStreamReader#41cd2648; line: 1, column: 1]
I've been stuck on this "dynamic data" problem for 2 days now. Already tried to use GSON library and a lot of other things but to no avail. So I'd like to ask:
- Why is Jackson causing this? Is this a bug? Some users have already asked this question but the provided solutions did not work for my case.
- Is this the correct way of handling data of which part is dynamic?
I've tested my code without the use of polymorphic classes (and only retrieving 1 User from the API) and object maps perfectly. The problem is caused by the polymorphism, but I cannot figure out how to fix it.
The polymorphism configuration you're specifying in the Jackson annotations there is suggesting a {"classType":"ResultObject",...} or {"classType":"ResultList",...} is going to be in the JSON-- which it isn't. I'm not sure of the exact cause of the error you're receiving, but it seems to be looking for the creator on the abstract class since there is not type property.
[For polymorphism, Jackson needs something to read from the JSON to determine what type of bean to deserialize at this point: you don't really have one, just the array/objectness of users. I think therefore that Jackson's polymorphism support isn't a good fit for this situation]
In fact, to allow a property to either take a single item or a list of items, you just need to enable the ACCEPT_SINGLE_VALUE_AS_ARRAY deserialization feature. However, this needs to be enabled globally, there doesn't seem to be a way to target it to a specific property.
public static final class UserResponse {
public String name;
public Results results;
public static final class Results {
public int count;
public List<User> users;
}
public static final class User {
public String username;
public int age;
}
}
#Test
public void reads_single_result() throws Exception {
ObjectReader reader = new ObjectMapper().reader(UserResponse.class)
.with(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
.with(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES).with(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
UserResponse response = reader.readValue("{ name: 'API name', results: { count: 1,"
+ " users: { username: 'username', age: 15 } } }");
assertThat(response.results.users, iterableWithSize(1));
assertThat(response.results.users.get(0).username, equalTo("username"));
}
#Test
public void reads_two_results() throws Exception {
ObjectReader reader = new ObjectMapper().reader(UserResponse.class)
.with(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
.with(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES).with(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
UserResponse response = reader.readValue("{ name: 'API name', results: { count: 2,"
+ " users: [{ username: 'username1', age: 18 }, { username: 'username2', age: 19 }] } }");
assertThat(response.results.users, iterableWithSize(2));
assertThat(response.results.users.get(0).username, equalTo("username1"));
assertThat(response.results.users.get(1).username, equalTo("username2"));
}
Oh, and if you want to get rid of the useless results object in there, you can do that with a converter:
public static final class UserResponseWithConverter {
public String name;
#JsonProperty("results")
#JsonDeserialize(converter = ConvertResultsToUserList.class)
public List<User> users;
public static final class Results {
public int count;
public List<User> users;
}
public static final class User {
public String username;
public int age;
}
public static final class ConvertResultsToUserList extends StdConverter<Results, List<User>> {
#Override
public List<User> convert(Results value) {
return value.users;
}
}
}
Configuring for correct serialization is left as an exercise for the reader ;)

Categories