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.
Related
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;
}
}
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'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'm developing a service using apache thrift. I have a service named getUser which returns User object. I couldn't find any way to define user-defined data type as a return type for my service defined in .thrift file.
user.thrift file looks like:
service UserService
{
User getUser(1:i32 userId),
}
When I am compiling the user.thrift to generate java source code, I am getting "Type "User" has not been defined" error. Can anyone please help me, how to represent this user-defined java object as a data type in thrift.
The getUser method code in service implementation class:
#Override
public User getUser(int user id) throws TException {
// here is the code that fetch the user object from database
return user;
}
This is my User class, whose object is being returned by service getUser:
public class User {
private int userId;
private String name;
private String city;
private String country;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
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;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
The relevant IDL could look like this:
struct User {
1 : i32 userId
2 : string name
3 : string city
4 : string country
}
So that's pretty straightforward. With that, you have two options:
use the Thrift-generated class as the data object, replacing your existing class
write some code that converts the data back and forth.
Both options have their pros and cons. With the first approach, you will lose the getter-only for the Id, because the field must be read/writable. But you don't have to convert any data.
The second approach leaves you with the getter/setter structure you have right now, with some minor modifications (the factory pattern could be worth a look). You pay that with the burden of additional data conversion from Thrift into your class and back.
It depends on the exact requirements, which option is the better one for your case.
You want to create a program for the management of a telephone book.
For each person are provided for the following personal information:
Surname
Name
Title
E-mail address (can not contain spaces and must contain the # symbol)
Company
Position
For every person you can store the following telephone numbers (one for each category)
Home
Office
Mobile Phone.
You can also store a list of other phone numbers. For each of the other numbers, you must store over to the phone number
a description of the number.
Here it is a homework I have to make for this evening in Java.
My issue is how I can implement the various category: Home,Office,ecc...Which is the best solution to implement those category? maybe an enum?
Here it is my implementation:
import java.util.*;
public class Persona {
private String Name;
private String surname;
private String title;
private String mail_addr;
private String company
private String position;
private Phone homePhone;
private Phone officePhone;
private Phone mobilePhone;
private Collection<Phone> otherphonesList
public Persona(String n,String s,String t,String m,String c,String p,Phone hp,Phone of,Phone mp,Collection<Phone> otherphones)
{
name=n;
surname=s;
title=t;
mail_addr=m;
company=c;
position=p;
homePhone=hp;
officePhone=of;
mobilePhone=mp;
otherphonesList=new ArrayList<Phone>(otherphones);
}
public String getName()
{
return name;
}
public String getSurname()
{
return surname;
}
public String getTitle()
{
return title;
}
public String getMail()
{
return mail_addr;
}
public String getCompany()
{
return company;
}
public String getPosition()
{
return position;
}
}
public class Phone {
private String phone;
private String description;
public Phone(String phone,String description)
{
this.phone=phone;
this.description=description;
}
public String getPhone()
{
return phone;
}
public String getDescription()
{
return description;
}
}
You can write a PhoneBook class with fields you need:
public class PhoneBook {
private Phone homePhone;
private Phone officePhone;
private Phone mobilePhone;
private List<Phone> otherPhones;
..getters/setters..
}
public class Phone {
private String phone;
private String description;
..getters/setters..
}
enums are a good solution if:
The number of items is fixed
All the information of the item is fixed as well (no loading of external files/resources/etc).
In a real application, you probably need to display the category on a display. This includes translating the category into the user's language which means there is an external dependency.
In such a case, you would use the enum as a key for a factory that gives you the text for each entry in the enum. The factory decouples your constant enum from the variables in the real world (like different/changing translations in the UI).
You may have a look at the Map class to store the phone numbers and add accessors for home, office and mobile phone entries.