I've got MultivaluedMap of custom object types which I am serializing using Gson. One of my requirement is to change the name of one of the object based on the length of the string to some other name.
I know that we can use annotations #SerializedName but it provides option for only one name alternative whereas I'm looking for two names for the same attribute and use it dynamically when serializing based on the string length.
How can I accomplish this?
Here's the outline of my custom object types:
Toplevel Class:
public class CompleteData{
private String country,
private String appId,
Private String userId,
private List<RecipientInfo> recipients;
private CustomDocument document;
<<setters//getters>>
public CompleteData(String country, String appId, String userId, List<RecipientInfo> recipients, CustomDocument document){
this.country=country..
..
..
}
CustomDocument Class:
public class CustomDocument{
String name;
String pageCount;
public CustomDocument(String name, int pageCount){
this.name = name;
this.pageCount = pageCount;
}
RecipientInfo Class:
public class RecipientInfo{
#serializedName("fullName")
String name;
String phoneNum;
public RecipientInfo(String name, String phoneNum){
this.name = name;
this.phoneNum = phoneNum;
}
}
Now I create List<CompleteData> completeData = new ArrayList<>();
Gather all the necessary information and add it to a MultivaluedMap as there are duplicate keys involved:
MultiValuedMap(<String, List<CompleteData>)
Now while using Gson to serialize this object, I want to change the "name" attribute in RecipientInfo class to be able to change dynamically based on the string length as fullname if the length is (>10 and <15) and fullNamewithSalu if the length is >20
Should I create a new class all together for this small change or is there a way I can serialize this object using Gson dynamically ?
Please help!
Thank you!
Related
I am following this article https://quarkus.io/guides/rest-client to build a REST Client to parse the output from the restcountries.eu service.
Here the class holding the model:
public class Country {
public String name;
public String alpha2Code;
public String capital;
public List<Currency> currencies;
public static class Currency {
public String code;
public String name;
public String symbol;
}
}
Now, suppose I would like to add a custom fields such as timestamp, to record the instant when this object has been created. I imagine, I would go ahead and add another field like below:
public class Country {
public String name;
public String alpha2Code;
public String capital;
public List<Currency> currencies;
public Instant timestamp; //<--------- added attribute
[....]
My question is: how do I tell the client to populate that field? Normally, I would have done it in the constructor. However, I could not find docs that explain this part.
Thanks for your help
Simone
You can actually do this in the default constructor. Frameworks like JSONB or Jackson expect POJOs to have a default constructor. They will call it when they create an instance of Country.
Use the #JsonbTransient or #JsonIgnore annotations to mark that attribute of your POJO as ignorable in order to avoid the unmarshaller complaining about attributes that cannot be found in the response.
#Data
public class Country {
private String name;
private String alpha2Code;
private String capital;
private List<Currency> currencies;
#JsonbTransient // if you're using JSONB (default in Quarkus)
#JsonIgnore // if you're using Jackson
private Instant timestamp;
public Country() {
this.timestamp = Instant.now();
}
PS The #Data annotation is something you should consider using. Encapsulation is not a bad thing but creating getters/setters is tedious. But Project Lombok certainly helps here.
Need some help here! I have a Java Rest API which is getting data from a .net endpoint and passing it on to the UI. The JSON properties are in capital case and I want to convert them in JAVA before sending it to the UI. Any pointers on this?
In java, I have a class like below:
public class Person {
#JsonProperty("Name")
private String name;
#JsonProperty("Age")
private int age;
}
I am using #JsonProperty as keys in .net are starting with capitalCase. How can I convert this back before sending it to the UI in Java?
Thanks for the help!
Create another class with the same structure and use there other names that you want. Something like this:
// Class to read .NET object
public class Person {
#JsonProperty("Name")
private String name;
#JsonProperty("Age")
private int age;
}
// Class to represent the object in Java REST API
public class Person {
#JsonProperty("name")
private String name;
#JsonProperty("age")
private int age;
}
// Class to represent the object in Java REST API,
// in case you use some standard library that
// uses property names for JSON as is
public class Person {
private String name;
private int age;
}
Of course you should put these classes into different packages.
Your code can look as follows:
xxx.dotnet.Person dotnetPerson = doSomethingViaDotNet(...);
yyy.rest.Person restPerson = new yyy.rest.Person();
restPerson.setName(dotnetPerson.getName());
restPerson.setAge(dotnetPerson.getAge());
...
return restPerson;
If you decide to use MapStruct, your code may looks as follows:
#Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class );
yyy.rest.Person dotnetToRest(xxx.dotnet.Person dotnetPerson);
}
Since all attributes have the same names and types you don't need anything else in your mapper.
MapStruct will generate a class that implements this interface. Usage will be as follows:
restPerson = PersonMapper.INSTANCE.dotnetToRest(dotnetPerson);
I am creating a RESTful API using spring boot. I have the requirement where I need to make a request to the resource
/user/notification
Notification resource will accept values in bodyrequest and send the notification to users.
#ResponseBody
#RequestMapping(value = "/user/notification", method = RequestMethod.POST)
public NotificationResponse sendNotification(#Valid #RequestBody NotificationRequest notificationRequest){
// here is code where I need to build right
// object of type text/file/link/map (please read full question below)
notificationService.send(notificationRequest.getUsername(), object);
}
It accepts: username and data for notification. Here is NotificationRequest class:
public class NotificationRequest {
#NotEmpty
private String username;
#NotEmpty
private String type;
private String title;
#NotEmpty
private String content;
private String url;
private String longitude;
private String latitude;
private String file_url;
//getters and setters
}
I have 4 types of notifications ie. text, link, map and file. And their attributes are these.
text
- type
- title
- content
link
- type
- title
- content
- url
map
- type
- title
- longitude
- latitude
file
- type
- title
- content
- file_url
I created 4 classes for these so I can create the right object, As you can see type and title are common attributes so I used inheritance.
public class NotificationBase {
private String type;
private String title;
//getters and setters here
}
And extended other 4 classes like this.
public class TextNotification extends NotificationBase {
private String content;
//getters and setters here
}
My question is, How I create my classes so that
if someone wants to send text notification I would able to get an object of TextNotification and if someone wants to send file notification that I would able to create FileNotification object?
Note: Please note I do not want to use gJson or Jackson to create JSON objects in this case.
Please do let me know if I need to add any more information here.
You can use jackson annotation for subtyping. In your case, you have the field type that will represent what is the type of the object that you expect.
So it's better to have an abstract NotificationRequest class only with needed fields:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = TextNotificationRequest.class, name = "text"),
#JsonSubTypes.Type(value = LinkNotificationRequest.class, name = "link")})
public abstract class NotificationRequest {
#NotEmpty
private String username;
#NotEmpty
private String type;
private String title;
public abstract NotificationResponse buildNotificationResponse();
}
And the implementation of each type that you need.
public class TextNotificationRequest extends NotificationRequest {
public String content;
public NotificationResponse buildNotificationResponse{
return new TextNotificationResponse(content);
}
}
Now you can just do something like this
#ResponseBody
#RequestMapping(value = "/user/notification", method = RequestMethod.POST)
public NotificationResponse sendNotification(#Valid #RequestBody NotificationRequest notificationRequest){
NotificationResponse response = notificationRequest.buildNotificationResponse();
notificationService.send(notificationRequest.getUsername(), response);
}
Here is some explanation. By setting these annotations your are saying to jackson which class to instantiate based on the value of type field. If type == "text" jackson will instantiate TextNotificationRequest and so on. And now you can use the power of polymorphism to create NotificationResponse and avoid if-else statements.
Also jackson allow subtyping based not only on the field value. You can read in more detailed info here #JsonTypeInfo
In my Android project I have two types of response where both response are identical except two keys.
Response 1
{"fullName":"William Sherlock Scott Holmes","address":"221B Baker Street, London, England, UK","downloads":642,"rating":3,"repos":["https://link1","https://link2","https://link3"]}
Response 2
{"name":"Sherlock","city":"London","downloads":642,"rating":3,"repos":["https://link1","https://link2","https://link3"]}
If you see the responses only two key names are changing fullName/name and address/city
I don't want to create one more pojo for other response. My question is: is it possible to use only one Pojo to read both responses?
public class AccountInfo {
private String name;
private String city;
//other objects
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
//other setters and getters
}
Any help will be appreciated...
You can annotate the members to accept values from two different json names using the #SerializedName annotation:
#SerializedName(value = "name", alternate = {"fullName"})
private String name;
#SerializedName(value = "city", alternate = {"address"})
private String city;
Either named element can then be placed into the members that are annotated like this.
UPDATED :
#SerializedName alternate names when deserializing is added in Version 2.4
Yes, you can totally use one POJO class for deserializing both responses. Your POJO class will contain keys from both responses.
public class Response {
private String name;
private String city;
private String fullName;
private String address;
private Integer downloads;
private Integer rating;
private List<String> repos ;
}
But when using the Response class, be careful that for first response, the name and city will be null, and for the second one, the address and fullname.
Yeah you can do that in a single POJO. Try this:
public class POJO {
#SerializedName("name")
public String name;
#SerializedName("city")
public String city;
#SerializedName("fullName")
public String fullName;
#SerializedName("address")
public String address;
#SerializedName("downloads")
public Integer downloads;
#SerializedName("rating")
public Integer rating;
#SerializedName("repos")
public List<String> repos = new ArrayList<String>();
}
While parsing you have to check values for null. For eg -
While Parsing Response 1: name and city variables will be null
While Parsing Response 2: fullname and address will be null
Note : Try checking values for null before using else you'll get nullpointerexception
Define all possible fields in your POJO Class like
public class AccountInfo {
private String name;
private String city;
private String fullname;
private String address;
}
While performing operation check for null in those feilds
I am using Selma and I have the following class:
public class Customer
{
private int id;
private String email;
private String firstName;
private String lastName;
private Date registeredDate;
private List<Address> addresses;
}
I want to map it to:
public class Customer
{
private String id;
private String email;
private String firstName;
private String lastName;
private Date registeredDate;
private String company;
private Address address1;
private Address address2;
}
Is there any way to cast the (int) id to (String) id and to set the first address from the List to address1 and the second address to address2?
I was thinking of using an interceptor, but this way I will have to manually map the Address classes. Is there a way to use Selma to automatically map the address classes in the interceptor? For example:
public class CustomerCustomMapper
{
public void interceptMyCustomerToCustomer(com.mycode.domain.Customer source, Customer destination) {
if(source.getAddresses() != null && source.getAddresses().size() > 0)
{
com.mycode.domain.Address myAddress1 = source.getAddresses().get(0);
AddressMapper addressMapper = Selma.builder(AddressMapper.class).build();
Address address1 = addressMapper.mapAddress(myAddress1);
destination.setAddress1(address1);
// do the same for address2
}
}
EDIT
Regarding mapping the Address class I did it the way I showed above. I created an AddressMapper with the following code inside: Address toAddress(com.mycode.domain.Address address);
Then I created an addressMapper and used it to map the Address autimatically:
AddressMapper addressMapper = Selma.builder(AddressMapper.class).build();
As for the id, currently I have to create an interceptor for every class that I map (almost every class has an Id in it (SQL)), and manually set the id like so: destination.setId(Integer.toString(source.getId()));
It is quite frustrating actually, but sadly I can't find a better option.
for the list to Address the interceptor is the good answer.
For the int to String you can specify a custom mapper mapping from int to String (see http://www.selma-java.org/#custom-mapper).
But I would recommend using an abstract mapper instead of building a new mapper to map the Address. This way you'll be able to integrate your specific code inside the mapper and call the address mapper method directly (see http://www.selma-java.org/#abstract-mapper).
For the toString thing, you can also add a feature request to Github. I'll be glad to add this feature.