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.
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 am trying to learn how to implement a CRUD ReST API. I created a simple application using JAX-RS and am testing my HTTP methods using Postman. My question is, how do I define optional fields for a model for POST method?
ex. Person contains firstName, lastName as required fields, and age, gender as optional fields.
#XmlRootElement
public class Person {
private long id;
private String firstName;
private String lastName;
public Person() {}
public Person(long id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
//getter and setters omitted
Above is a sub-resource to another resource, and below addPerson service method is used to POST to a HashMap.
public Person addPerson(long userId, Person person) {
Map<Long, Person> persons = users.get(userId).getPersons();
person.setId(persons.size() + 1);
persons.put(person.getId(), person);
return person;
}
I thought of constructing a model whose constructor has multiple different combination of parameters to instantiate a model class, but that doesn't seem very efficient. Can somebody advise?
FURTHER EDIT: This needs to be done while processing JSON. Given two JSON formatted request:
{
"firstName": "Jay",
"lastName": "Kay",
"age": "13"
}
and
{
"firstName": "Jay",
"lastName": "Kay"
}
both should be processable since age(as well as gender) is an "optional attributes" so it may be either omitted, or have a value. I'm coming across JSON parsers and trying to read through them since that may be an answer to processing such requests.
Add gender and age to your Person class
#XmlRootElement
public class Person {
private long id;
private String firstName;
private String lastName;
private int age;
private String gender;
public Person() {}
public Person(long id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
}
While making request from Postman, include the age and gender as keys in x-www-form-urlencoded.
By default, the value of integer is 0 and value for String is empty string.
In your method to handle the request, check the values for gender and age.
If the values are provided in the request, then use the setters of Person class to set the values accordingly.
Suppose the following is your method to handle the request :
#POST
#Path("/AddPerson")
#Produces(MediaType.TEXT_PLAIN)
public String addPerson(#FormParam("id") long id,
#FormParam("firstName") String firstName,
#FormParam("lastName") String lastName,
#FormParam("age") int age,
#FormParam("gender") String gender) {
Person person = new Person(id,firstName,lastName);
if(age != 0) {
person.setAge(age);
}
if(!gender.equals("")) {
person.setGender(gender)
}
// Assuming that PersonService class has the addPerson method
PersonService.addPerson(1,person)
return "Person with id "+ id + " added";
}
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
Evening,
I retrieve JSON data from a server in this format:
Json-Book:
{
_id: String,
isbn: String,
owner: String username,
rentedTo: Array[String usernames]
}
Json-Crowd:
{
_id: String,
isbn: String,
owner: String username,
availableForRent: Integer,
rentedTo: Array[String usernames]
}
Json-User:
{
username: String,
books: Array[Book],
crowds: Array[Crowd]
}
Similarly, I have three classes:
public class Book{
private String _id;
private String isbn;
private User owner;
private ArrayList<User> rentedTo;
public Book(String _id, String isbn, User owner, ArrayList<User> rentedTo) {
this._id = _id;
this.isbn = isbn;
this.owner = owner;
this.rentedTo = rentedTo;
}
}
public class Crowd {
private String _id;
private String name;
private User owner;
private ArrayList<User> members;
public Crowd(String _id, String name, User owner, ArrayList<User> members) {
this._id = _id;
this.name = name;
this.owner = owner;
this.members = members;
}
}
public class User {
private String name;
private Shelf shelf;
private ArrayList<Book> books;
private ArrayList<Crowd> crowds;
public User(String name, ArrayList<Book> books, ArrayList<Crowd> crowds) {
this.name = name;
this.booksOwned = books;
this.crowds = crowds;
}
}
As you can see, all the field names match up, but not all of the types. Users in the keys owner and rentedTo in Json-book and Json-crowd have values of type String and ArrayList<String> respectively, where the strings are unique usernames. In the classes, these fields are of type User and ArrayList<User>. In and of itself, this is no problem, because I can get the User object from the string with this method:
public User getUser(String username) {
return users.get(username);
}
Now, I'm a bit confused as to what deserializers I need in order to get proper objects from the JSON data. How would the, uh, architecture of this look? Deserializers for each class Book, Crowd and User, each of which fetches one field at a time from the Json-data and calls the constructor?
I can't wrap my head around how this would work together. The deserializer for User would need to use/reference the deserializer for Crowd, but do I another deserializer since the objects are in array?
I assume I would need an opposite set of serializers in order to get the classes into Json-data of the correct format.
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());
....
}
};