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
Related
I have an input object as
class Person {
private String name;
private String email;
private String phone;
private Address address;
public static class Address {
private String city;
private String pincode;
private String street;
private AddrDetails details;
public static class AddrDetails {
private String state;
private String country;
}
}
}
I am using vavr Validations to validate the input
public static Validation<Seq<ConstraintViolation>, PersonDetailsModel> validatePerson(PersonDetailsRequest request) {
Validation
.combine(
validateName("name", request.getName()),
validateEmail("email", request.getEmail()),
validatePhone("phone", request.getPhone()),
validateAddress(request.getAddress())
).ap((name, email, phone, address) -> new PersonDetailsModel(name, email, phone, address);
}
public static Validation<Seq<ConstraintViolation>, Person.Address> validateAddress(
Person.Address request) {
return Validation
.combine(..
).ap((..) -> new Person.Address(..);
}
In the second function, it returns Seq of ConstraintViolation while validatePerson expects only ConstraintViolation which is why it is failing although I have to add one more level of nesting of validations for AddrDetails. How to handle nested objects validations with this approach.
I am not sure about how shall I go ahead?
In our project we call .mapError(Util::flattenErrors) after .ap. I have the feeling that there is a better way, but this at least solves the nesting.
The method in the Util class looks like this :
public static Seq<ConstraintViolation> flattenErrors(final Seq<Seq<ConstraintViolation>> nested) {
return nested
.flatMap(Function.identity())
.distinct(); // duplicate removal
}
I'm building a POJO class to match some requests coming my way in a REST API implemented on SpringBoot. Some of the data has to be given to me, otherwise I am not willing to even serve the request. To make sure the client gives me at least what I absolutely need, I have used Lombok's #NonNull:
#Data
public class ProductRequestBody implements Serializable
{
private String name;
private String category;
private String description;
private String abbreviation;
private String labelColor;
private Double cost;
public ProductRequestBody()
{
}
#JsonCreator
public ProductRequestBody(#NonNull #JsonProperty("name") String name,
#NonNull #JsonProperty("category") String category,
#JsonProperty("description") String description,
#NonNull #JsonProperty("cost") Double cost)
{
this.name = name;
this.category = category;
this.description = description;
this.cost = cost;
}
}
(I completely understand that handling monetary quantities as Doubles is a no-no; this is just an example.
Processing this from my controller is as easy as a listener on the /products endpoint like so:
#PostMapping(value = "/products")
public Product postProduct(#RequestBody ProductRequestBody newProduct)
{
// ...
// Serve the request appropriately
// ...
}
Now, if I receive a POST request with a null field that has not been marked as #NonNull, like the following, I can serve it without issue:
{
"name": "Some Product Name",
"category": "Some Product Category",
"cost" : 10.0,
"description": null
}
My goal, however, is to be able to handle JSON requests that simply don't even have the fields they don't care about. That is, I want to be able to serve the following as well, and I currently can't:
{
"name": "Some Product Name",
"category": "Some Product Category",
"cost" : 10.0,
// No "description" field in this payload
}
How could I go about doing this? The less code, the better, as always.
If you use spring-boot and lombok you can simplify your class to be like this:
#Data
public class ProductRequestBody implements Serializable {
#NonNull
private String name;
#NonNull
private String category;
#NonNull
private Double cost;
private String description;
private String abbreviation;
private String labelColor;
}
it will return 400 if name, category or cost will be not provided and 200 otherwise.
Spring handles serializing and deserializing json without any issue.
You should let spring handle it.
You can try the following.
#Data
public class ProductRequestBody implements Serializable {
#NonNull
private String name;
#NonNull
private String category;
private String description;
private String abbreviation;
private String labelColor;
#NonNull
private Double cost;
}
If you really want to follow the pattern of creating a constructor, then you should create a constructor with only the #NonNull fields and create getter of others (lombok handles that for you). If you want to add #JsonProperty then you need to create separate getter.
#Data
public class ProductRequestBody implements Serializable
{
private String name;
private String category;
private String description;
private String abbreviation;
private String labelColor;
private Double cost;
public ProductRequestBody()
{
}
#JsonCreator
public ProductRequestBody(#NonNull #JsonProperty("name") String name,
#NonNull #JsonProperty("category") String category,
#NonNull #JsonProperty("cost") Double cost)
{
this.name = name;
this.category = category;
this.cost = cost;
}
#JsonProperty("description")
public String getDescription() {
return description;
}
}
I'm trying to learn about serialization and encountered the following problem:
I have an implementation of a customer that looks somewhat like this.
private static customerCount = 0;
private String customerID;
private String name;
private String street;
private String city;
private String postcode;
private String type;
I'm trying to serialize / deserialize an Arraylist
In the constructor, the ID will be created like this:
private Customer(...){
this.customerID = "ID" + customerCount;
customerCount++;
}
The serialization process works, however, all the IDs are set to ID0 when I deserialize.
Can anyone help resolve this problem?
Update: Alright, I just found out that static fields wont be serialized. How can I "model" the ID of a customer so I can serialize it? I need to have a unique value to create IDs for customers.
Here's a solution that combines the factory with the list that keeps track of customer count.
The customer class has a protected constructor, forcing you to build them through another means within the same package.
public class Customer implements Serializable {
private String customerID;
private String name;
private String street;
private String city;
private String postcode;
private String type;
protected Customer(String customerID,
String name,
String street,
String city,
String postcode,
String type) {
this.customerID = customerID;
this.name = name;
this.street = street;
this.city = city;
this.postcode = postcode;
this.type = type;
}
}
Now within the package, create a list wrapper like this:
public class CustomerList {
private int customerCount = 0;
private List<Customer> customers = new ArrayList<>();
public boolean addCustomer(String name,
String street,
String city,
String postcode,
String type) {
Customer customer = new Customer("ID" + customerCount++,
name,
street,
city,
postcode,
type);
return customers.add(customer);
}
}
This class then takes care of constructing the new customer, and provides a unique ID.
Edit: Just noticed that you now also have the upside of making the CustomerList class serializable as well. Then you can load it and still have an accurate customer count for adding additional uniquely ID-ed customers.
Usually, you would like to serialize only attributes and their values, not the logic from the class. Logic should happen before serialization or after deserialization.
I have some old code which is working fine but now i want to use #JsonCreator in domain objects and i am able to re-factor all stuff but i am now stucked in one issue. In one scenario i have Json Structure like:
[{
"name" : "John",
"emailAddress" :"stucked#gmail.com"
}]
But in java backend i have structure like:
class Employee {
public final String name;
public final EmailAddress emailAddress;
Employee(final String name, final EmailAddress emailAddress) {
this.name = name;
this.emailAddress = emailAddress;
}
Employee() {
this(null, null)
}
}
class EmailAddress {
public final String address;
EmailAddress(final String address) {
this.address = address;
}
EmailAddress() {
this(null);
}
}
So I am trying like :
class Employee {
public final String name;
public final EmailAddress emailAddress;
#JsonCreator
Employee(#JsonProperty("name")final String name,
#JsonProperty("emailAddress")final EmailAddress emailAddress) {
this.name = name;
this.emailAddress = emailAddress;
}
}
class EmailAddress {
public final String address;
#JsonCreator
EmailAddress(#JsonProperty("address")final String address) {
this.address = address;
}
}
Now the problem is in using #JsonProperty with "emailAddress" field in Employee domain object and then #JsonProperty inside EmailAddress Object with "address" because in Json i dont have any "address" property and due to which JsonParsing error is coming.
Well problem can be easily solved if i declare emailAddress as String instead of EmailAddress and remove EmailAddress object but address field of EmailAddress object is used at many other places, modification of which require lot of effort and there are lots of such cases. Is there any other solution??
So what exactly is your problem here? If the goal is to allow both of these:
"stucked#gmail.com"
{ "address" : "stucked#gmail.com" }
it should already work: Jackson figures out that single-String constructor may be used if JSON String is found; and setters/fields if a JSON Object is found. You just need to ensure names match ("address" vs "emailAddress").
I have a situation, where I need to map multi objects (in a flat structure) into one object (an hierarchy object) in Java using ModelMapper.
For example,
class Person{
String name;
int age;
}
class Address{
int streetSumber;
String streetName;
String city;
}
class Phone{
String type;
String number;
}
What I want to get out from the model mapper is something like
class PersonDTO{
String name;
int age;
class AddressDTO{
int streetSumber;
String streetName;
String city;
}
class PhoneDTO{
String type;
String number;
}
}
Is there a way to simple achieve this?
Thanks
You must create a PersonWrapper
class PersonWrapper {
Person person;
Address address;
Phone phone;
}
and map PersonWrapper to PersonDTO, for example
PropertyMap<PersonWrapper, PersonDTO> orderMap = new PropertyMap<Order, OrderDTO>() {
protected void configure() {
map().setName(source.getPerson().getName());
....
}
};