Convert POJO to POJO with custom converter using json configuration - java

We have two Pojo files.
Person {
String name;
int age;
String address;
String phoneNo;
boolean isMarried;
}
and
OtherPerson {
//mandatory fields are name and age
String name_other;
int age_other;
//other fields
Map<String, Object> otherFields;
}
and a json file which defines the mapping between the fields using name
mappingJson {
"name":"name_other",
"age":"age_other",
"address":"address_other",
"phoneNo":"phoneno_other",
"isMarried":"ismarried_other"
}
Please let me know the best approach to convert Person to OtherPerson. So that the mandatory fields map to name_other and age_other while the other fields should be added to the map(otherFields)
It may be
Person->Person(json)->OtherPerson
Or Person->OtherPerson.
EDIT:
"Use case: We have an API which used to accepts a POJO 'A' but now it needs to accept POJO 'B' as an input argument. This POJO needs to get converted into POJO 'A' which can then be used for persisting into the database. Also POJO 'B' is not under our control"

That's a perfect fit for Jackson Converter! :)
It could work like this:
class OtherPerson {
#JsonProperty("name")
public String name_other;
#JsonProperty("age")
public int age_other;
Map<String, Object> otherFields = new LinkedHashMap<>();;
#JsonAnySetter
public void add(String key, Object value) {
otherFields.put(key, value);
}
}
// ...
Person person = new Person();
person.name = "Avinash";
person.age = 25;
person.address = "Mumbai";
person.phoneNo = "910731";
person.isMarried = true; // :( sorry ladies!
// ...
ObjectMapper mapper = new ObjectMapper();
// If we cannot put #JsonAutoDetect on top of Person.class,
// we need to add handling of non-public fields
// since Person seems to neither have public fields nor setters
mapper.configOverride(Person.class)
.setVisibility(JsonAutoDetect.Value.defaultVisibility()
.withFieldVisibility(JsonAutoDetect.Visibility.NON_PRIVATE));
OtherPerson other = mapper.convertValue(person, OtherPerson.class);
Voilà!

I personally would do this without JSON. It's my understanding that some fields in the Map are optional while name and age are mandatory. In the case of the optional content, I would use the Ternary operator to create the person object. This allows you to add some default value if the optional field is not available.
Main
import java.util.HashMap;
import java.util.Map;
/**
*
* #author blj0011
*/
public class JavaApplication30 {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
Map<String, Object> map1 = new HashMap();
map1.put("address", "123 Hello Street");
map1.put("phoneNo", "555-555-5555");
map1.put("isMarried", true);
OtherPerson otherPerson = new OtherPerson("John Doe", 22, map1);
Map<String, Object> map2 = new HashMap();
map2.put("address", "4456 Bye Road");
map2.put("isMarried", false);
OtherPerson otherPerson2 = new OtherPerson("Jane Doe", 21, map2);
Person person1 = new Person(otherPerson.getName_other(), otherPerson.getAge_other(),
otherPerson.getOtherFields().containsKey("address") ? otherPerson.getOtherFields().get("address").toString(): "",
otherPerson.getOtherFields().containsKey("phoneNo") ? otherPerson.getOtherFields().get("phoneNo").toString(): "",
otherPerson.getOtherFields().containsKey("isMarried") ? Boolean.valueOf(otherPerson.getOtherFields().get("isMarried").toString()): false);
System.out.println(person1);
Person person2 = new Person(otherPerson2.getName_other(), otherPerson2.getAge_other(),
otherPerson2.getOtherFields().containsKey("address") ? otherPerson2.getOtherFields().get("address").toString(): "",
otherPerson2.getOtherFields().containsKey("phoneNo") ? otherPerson2.getOtherFields().get("phoneNo").toString(): "",
otherPerson2.getOtherFields().containsKey("isMarried") ? Boolean.valueOf(otherPerson2.getOtherFields().get("isMarried").toString()): false);
System.out.println(person2);
}
}
Person
/**
*
* #author blj0011
*/
public class Person {
private String name;
private int age;
private String address;
private String phoneNo;
private boolean isMarried;
public Person(String name, int age, String address, String phoneNo, boolean isMarried) {
this.name = name;
this.age = age;
this.address = address;
this.phoneNo = phoneNo;
this.isMarried = isMarried;
}
public boolean isIsMarried() {
return isMarried;
}
public void setIsMarried(boolean isMarried) {
this.isMarried = isMarried;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhoneNo() {
return phoneNo;
}
public void setPhoneNo(String phoneNo) {
this.phoneNo = phoneNo;
}
#Override
public String toString() {
return "Person{" + "name=" + name + ", age=" + age + ", address=" + address + ", phoneNo=" + phoneNo + ", isMarried=" + isMarried + '}';
}
}
OtherPerson
/**
*
* #author blj0011
*/
public class OtherPerson {
//mandatory fields are name and age
private String name_other;
private int age_other;
//other fields
private Map<String, Object> otherFields;
public OtherPerson(String name_other, int age_other, Map<String, Object> otherFields) {
this.name_other = name_other;
this.age_other = age_other;
this.otherFields = otherFields;
}
public Map<String, Object> getOtherFields() {
return otherFields;
}
public void setOtherFields(Map<String, Object> otherFields) {
this.otherFields = otherFields;
}
public String getName_other() {
return name_other;
}
public void setName_other(String name_other) {
this.name_other = name_other;
}
public int getAge_other() {
return age_other;
}
public void setAge_other(int age_other) {
this.age_other = age_other;
}
}
Output
Person{name=John Doe, age=22, address=123 Hello Street, phoneNo=555-555-5555, isMarried=true}
Person{name=Jane Doe, age=21, address=4456 Bye Road, phoneNo=, isMarried=false}
As you can see in the output OtherPerson2 did not have a phone number. Empty string was use as the default value.

Related

Keep null fields of a POJO as keys in map java

I have a POJO and using object mapper to convert it into a map. I want the map to contain all fields of the POJO in it's entryset even if their value is null. So that entry might be null in the map I am creating.
Map<String, Object> oldDetails = objectMapper.convertValue(oldFields, Map.class);
I've tried using below snippet but that didn't help me.
objectMapper.setDefaultPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.ALWAYS, JsonInclude.Include.ALWAYS));
Any other idea that I can use here?
My POJO:
#JsonInclude(NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class BaseFields {
#JsonProperty("number") protected String number;
#JsonProperty("name") protected String name;
#JsonProperty("dob") protected String dob;
}
There are other classes inheriting from BaseFields such as.
#JsonInclude(NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class CustomerBaseFields extends BaseFields {
#JsonProperty("email") protected String email;
}
Now there is method that converts an instance of ChangedBaseFields into a map.
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, true);
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
objectMapper.setDefaultPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.ALWAYS, JsonInclude.Include.ALWAYS));
Map<String, Object> oldDetails = objectMapper.convertValue(baseFields, Map.class);
But say if a field like email is null in object, I'm not getting a key email in my map.
Here's simple code demonstrating that your code in fact works without any additional configuration with default ObjectMapper.
package java17test;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
public class Test {
public static void main(String[] args) {
var objectMapper = new ObjectMapper();
var oldFields = new TestClass();
oldFields.setFirstName("first name");
Map<?, ?> oldDetails = objectMapper.convertValue(oldFields, Map.class);
oldDetails.forEach((key, value) -> System.out.println(key + "=" + value));
}
static class TestClass {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
}
Output:
firstName=first name
lastName=null
You probably have an X-Y problem because you mentioned in a comment to vbezhenar's answer that your intention is to compare the fields of two objects of the same type, to find out which fields have changed.
You don't have to use Jackson to do that.
Here's a simple pure Java (no dependencies) way of comparing two objects:
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
public class Utils {
/**
* Compares two objects of the same type, returning the field values that are different.
*
* #param o1 an object
* #param o2 another object
* #return a map of field name to a two-element array of the two different values of o1 and o2
*/
public static <T> Map<String, Object[]> comparePojos(T o1, T o2) throws IllegalAccessException {
Map<String, Object[]> diffs = new LinkedHashMap<>();
for (Field field : o1.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object[] values = {field.get(o1), field.get(o2)};
if (!values[0].equals(values[1]))
diffs.put(field.getName(), values);
}
return diffs;
}
}
class UtilsDemo {
public static void main(String[] args) throws IllegalAccessException {
SamplePojo a = new SamplePojo("alice", 42);
SamplePojo b = new SamplePojo("bob", 42);
Utils.comparePojos(a, b).forEach((k, v) -> System.out.println(k + ": " + Arrays.toString(v)));
}
}
class SamplePojo {
public SamplePojo(String name, int age) {
this.name = name;
this.age = age;
}
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

How do I leverage a json mapping file to convert from one pojo to another pojo?

I have two POJOs (Person.java and User.java) that contain similar information. See below:
public class Person {
private String first_name;
private String last_name;
private Integer age;
private Integer weight;
private Integer height;
public String getFirst_name() {
return first_name;
}
public void setFirst_name(String first_name) {
this.first_name = first_name;
}
public String getLast_name() {
return last_name;
}
public void setLast_name(String last_name) {
this.last_name = last_name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
public Integer getHeight() {
return height;
}
public void setHeight(Integer height) {
this.height = height;
}
}
public class User {
private String name_first;
private String name_last;
private Integer my_age;
private Integer my_weight;
private String social_security;
public String getName_first() {
return name_first;
}
public void setName_first(String name_first) {
this.name_first = name_first;
}
public String getName_last() {
return name_last;
}
public void setName_last(String name_last) {
this.name_last = name_last;
}
public Integer getMy_age() {
return my_age;
}
public void setMy_age(Integer my_age) {
this.my_age = my_age;
}
public Integer getMy_weight() {
return my_weight;
}
public void setMy_weight(Integer my_weight) {
this.my_weight = my_weight;
}
public String getSocial_security() {
return social_security;
}
public void setSocial_security(String social_security) {
this.social_security = social_security;
}
}
I have defined a mapping.json file as shown below using GSON.
{
"columnMap": [
{
"userColumn": "name_first",
"personColumn": "first_name"
},
{
"userColumn": "last_first",
"personColumn": "first_last"
},
{
"userColumn": "my_age",
"personColumn": "age"
},
{
"userColumn": "my_weight",
"personColumn": "weight"
}
]
}
public class Mapping {
private ArrayList<Pair> columnMap;
public Mapping(){
columnMap = new ArrayList<>();
}
public ArrayList<Pair> getColumnMap() {
return columnMap;
}
public void setColumnMap(ArrayList<Pair> columnMap) {
this.columnMap = columnMap;
}
}
I am writing a utility class helper function that converts between a Person and User object the mapped pairs.
public class Pair {
private String userColumn;
private String personColumn;
public String getUserColumn() {
return userColumn;
}
public void setUserColumn(String userColumn) {
this.userColumn = userColumn;
}
public String getPersonColumn() {
return personColumn;
}
public void setPersonColumn(String personColumn) {
this.personColumn = personColumn;
}
public static void main(String args[]){
}
}
My question is below:
As you can see the returnVal object is being set by me (the programmer) to convert from a User POJO to a Person POJO. How do I leverage the pre-defined mapping.json to do this? The reason I am asking is in the future, the mapping.json file may change (maybe the weight mapping no longer exists). So I am trying to avoid re-programming this Utility.userToPerson() function. How can I achieve this? I am thinking Java reflection is the way to go, but I would like to hear back from the Java community.
public class Utility {
public static Person userToPerson(User u){
Person returnVal = new Person();
returnVal.setAge(u.getMy_age()); // <-- Question How do I leverage mapping.json here?
returnVal.setFirst_name(u.getName_first());
returnVal.setLast_name(u.getName_last());
returnVal.setWeight(u.getMy_weight());
return returnVal;
}
}
You can introspect the beans (i.e. User and Person) for the field names and call corresponding getter from User to fetch the value. Later call corresponding setter in Person.
Here I have taken userToPersonFieldsMap for mapping the field, you can load mapping from JSON file and construct the map accordingly.
Important code section is the for loop, where it dynamically calls getter and setter and does the job.
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
public class UserToPersonMapper {
public static void main(String[] args) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
Map<String, String> userToPersonFieldsMap = new HashMap<>();
userToPersonFieldsMap.put("name_first", "first_name");
userToPersonFieldsMap.put("last_first", "first_last");
userToPersonFieldsMap.put("age", "personAge");
//existing user
User user = new User("Tony", "Stark", 20);
//new person - to be initialised with values from user
Person person = new Person();
for (Map.Entry<String, String> entry : userToPersonFieldsMap.entrySet()) {
Object userVal = new PropertyDescriptor(entry.getKey(), User.class).getReadMethod().invoke(user);
new PropertyDescriptor(entry.getValue(), Person.class).getWriteMethod().invoke(person, userVal);
}
System.out.println(user);
System.out.println(person);
}
}
class User {
private String name_first;
private String last_first;
private int age;
public User(String name_first, String last_first, int age) {
this.name_first = name_first;
this.last_first = last_first;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName_first() {
return name_first;
}
public String getLast_first() {
return last_first;
}
public void setName_first(String name_first) {
this.name_first = name_first;
}
public void setLast_first(String last_first) {
this.last_first = last_first;
}
#Override
public String toString() {
return "User{" +
"name_first='" + name_first + '\'' +
", last_first='" + last_first + '\'' +
", age=" + age +
'}';
}
}
class Person {
private String first_name;
private String first_last;
private int personAge;
public void setFirst_name(String first_name) {
this.first_name = first_name;
}
public void setFirst_last(String first_last) {
this.first_last = first_last;
}
public String getFirst_name() {
return first_name;
}
public String getFirst_last() {
return first_last;
}
public int getPersonAge() {
return personAge;
}
public void setPersonAge(int personAge) {
this.personAge = personAge;
}
#Override
public String toString() {
return "Person{" +
"first_name='" + first_name + '\'' +
", first_last='" + first_last + '\'' +
", personAge=" + personAge +
'}';
}
}
You can tweak and try it out this example to make it more align with your requirement.
Note:
This solution uses reflection.

How to use BiConsumer to accept an object having an object, list of strings, and variables?

I am trying to use BiConsumer to accept an object that contains variables, an object and a list of strings in Java. I am not sure how to set the values into one object if using just BiConsumer. Maybe, if I tried to wrap Student object in a List and pass it into a new Student might help, but so far I get a null object. I haven't seen a lot of post with object containing just variables in one object and using BiConsumer.
#Test
public void testStudent() {
List<Object> objectList1 = new ArrayList<>();
Student student = new Student();
StudentLevel studentLevel = new StudentLevel("freshman", true);
List<String> studentLists = Arrays.asList("Maria", "Jose", "Juan");
Student student1 = new Student("Maria", "Lopez", "A", studentLevel, studentLists);
objectList1.add(student1);
BiConsumer<Object, List<Object>> biconsumer = (obj, list) -> {
for (Object object: list) {
// set and get but how?
// obj = object;
}
};
// To accept values from the object list see below for desired output
biconsumer.accept(student, objectList1);
// For debugging purpose
System.out.println("All Student: " + student);
}
public class Student {
private String name;
private String lastName;
private String grade;
private StudentLevel studentGrade;
private List<String> studentList;
public Student(final String name, final String lastName, final String grade, final StudentLevel studentGrade, final List<String> studentList) {
this.name = name;
this.lastName = lastName;
this.grade = grade;
this.studentGrade = studentGrade;
this.studentList = studentList;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
public StudentLevel getStudentGrade() {
return studentGrade;
}
public void setStudentGrade(StudentLevel studentGrade) {
this.studentGrade = studentGrade;
}
public List<String> getStudentList() {
return studentList;
}
public void setStudentList(List<String> studentList) {
this.studentList = studentList;
}
}
public class StudentLevel {
private String level;
private Boolean pass;
public StudentLevel(final String level, final Boolean pass){
this.level = level;
this.pass = pass;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public Boolean getPass() {
return pass;
}
public void setPass(Boolean pass) {
this.pass = pass;
}
}
Desired output:
student = {Student#887}
name = "Maria"
lastName = "Lopez"
grade = "A"
studentGrade = {StudentLevel#889}
level = "freshman"
pass = {Boolean#906} true
studentList = {Arrays$ArrayList#890} size = 3
0 = "Maria"
1 = "Jose"
2 = "Juan"
You are assigning local reference of object to obj (won't copy the values)
obj = object; // means, student = object
no change will be reflected outside the scope of the consumer, instead, you need to modify the state using setters as:
((Student) obj).setName(((Student) object).getName());
obj = object; // after this point, student object won't be accessible in the current scope.
Note: You should have getters and setters to access private properties outside Student class, and this example just demonstrates the working by assigning name property.
Reference:
• Classes and Object

Display multiple String items Java

I have a last Java homework task, this task is about employees,
my method should print employee's names and surnames, worked more than "n" years.
What I've done for now:
public class LastTask {
public static void main(String[] args) {
Employee employee1 = new Employee("Dobrobaba", "Irina", "Ivanovna",
"Moskva", 1900, 6);
Employee employee2 = new Employee("Shmal", "Anna", "Nikolaevna",
"Krasnodar", 2017, 8);
Employee employee3 = new Employee("Kerimova", "Niseimhalum", "Magomedmirzaevna",
"New-York", 2010, 3);
Employee employee4 = new Employee("Dobryden", "Yuri", "Viktorovich",
"Auckland", 2000, 11);
Employee employee5 = new Employee("Lopata", "Leonid", "Nikolaevich",
"Beijing", 2014, 11);
}
/**
* Prints employees' information, which have worked more than 'n' year(s) for now.
*
* #param n years quantity
* #return the String, contained surname, name, patronymic and address of the specific employee(s).
*/
public static String displayEmployees(int n) {
return null;
}
}
class Employee {
private String surname;
private String name;
private String patronymic;
private String address;
private int employmentYear;
private int employmentMonth;
Employee(String surname, String name, String patronymic, String address, int employmentYear, int employmentMonth) {
this.surname = surname;
this.name = name;
this.patronymic = patronymic;
this.address = address;
this.employmentYear = employmentYear;
this.employmentMonth = employmentMonth;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPatronymic() {
return patronymic;
}
public void setPatronymic(String patronymic) {
this.patronymic = patronymic;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getEmploymentYear() {
return employmentYear;
}
public void setEmploymentYear(int employmentYear) {
this.employmentYear = employmentYear;
}
public int getEmploymentMonth() {
return employmentMonth;
}
public void setEmploymentMonth(int employmentMonth) {
this.employmentMonth = employmentMonth;
}
}
I made a parametrised constructor for creating employees with multiple parameters, also made parameters encapsulated.
Have no clue what to do next, task says that I can use List/ArrayList, but after some time googling about it, I still can't understand how to implement a condition like if (employmentYear - currentYear >= n) then return employee1, employee4 for example.
Could you give me some tips?
Thank you for your attention.
You can create a static ArrayList and add those all employees to that ArrayList, and in displayEmployees method you can stream that list based on condition if employee EmploymentYear greater than n print details and add to another list so finally if you want you can just return count of employees or you can return List of employees also
public class LastTask {
static List<Employee> employee = new ArrayList<>();
public static void main(String[] args) {
Employee employee1 = new Employee("Dobrobaba", "Irina", "Ivanovna",
"Moskva", 1900, 6);
Employee employee2 = new Employee("Shmal", "Anna", "Nikolaevna",
"Krasnodar", 2017, 8);
Employee employee3 = new Employee("Kerimova", "Niseimhalum", "Magomedmirzaevna",
"New-York", 2010, 3);
Employee employee4 = new Employee("Dobryden", "Yuri", "Viktorovich",
"Auckland", 2000, 11);
Employee employee5 = new Employee("Lopata", "Leonid", "Nikolaevich",
"Beijing", 2014, 11);
employee.add(employee1);
employee.add(employee2);
employee.add(employee3);
employee.add(employee4);
employee.add(employee5);
}
/**
* Prints employees' information, which have worked more than 'n' year(s) for now.
*
* #param n years quantity
* #return the String, contained surname, name, patronymic and address of the specific employee(s).
*/
public static int displayEmployees(int n) {
List<Employee> finalList = new ArrayList<>();
employee.stream().forEach(emp->{
if(emp.getEmploymentYear()-Year.now().getValue()>=n) {
System.out.println("Employee Name : "+emp.getName()+" Sur Aame : "+emp.getSurname());
finalList.add(emp);
}
});
return finalList.size();
}
}
If you are looking for a way to find "worked more than 'n' years", this might help you.
Calendar.getInstance().get(Calendar.YEAR) - employmentYear >= n
Add a proper toString() method in the Employee class to get the desired output, apart from that I have used the filter() method from the Stream object to filter through the Employee objects. I am passing the number of years worked as an input parameter and calculating the years served in employment from the employmentYear field.
package com.company;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.stream.Collectors;
public class LastTask {
private static List<Employee> listEmps;
public static void main(String[] args) {
Employee employee1 = new Employee("Dobrobaba", "Irina", "Ivanovna",
"Moskva", 1900, 6);
Employee employee2 = new Employee("Shmal", "Anna", "Nikolaevna",
"Krasnodar", 2017, 8);
Employee employee3 = new Employee("Kerimova", "Niseimhalum", "Magomedmirzaevna",
"New-York", 2010, 3);
Employee employee4 = new Employee("Dobryden", "Yuri", "Viktorovich",
"Auckland", 2000, 11);
Employee employee5 = new Employee("Lopata", "Leonid", "Nikolaevich",
"Beijing", 2014, 11);
listEmps = new ArrayList<>(Arrays.asList(employee1,employee2,employee3,employee4,employee5));
//display employee details of employees who worked more than 17 years.
displayEmployees(17);
}
/**
* Prints employees' information, which have worked more than 'n' year(s) for now.
*
* #param n years quantity
* #return the String, contained surname, name, patronymic and address of the specific employee(s).
*/
public static void displayEmployees(int n) {
int year = Calendar.getInstance().get(Calendar.YEAR);
listEmps.stream()
.filter(emp ->{
return year - emp.getEmploymentYear() > n;
})
.collect(Collectors.toList())
.forEach(System.out::println);
}
}
class Employee {
private String surname;
private String name;
private String patronymic;
private String address;
private int employmentYear;
private int employmentMonth;
Employee(String surname, String name, String patronymic, String address, int employmentYear, int employmentMonth) {
this.surname = surname;
this.name = name;
this.patronymic = patronymic;
this.address = address;
this.employmentYear = employmentYear;
this.employmentMonth = employmentMonth;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPatronymic() {
return patronymic;
}
public void setPatronymic(String patronymic) {
this.patronymic = patronymic;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getEmploymentYear() {
return employmentYear;
}
public void setEmploymentYear(int employmentYear) {
this.employmentYear = employmentYear;
}
public int getEmploymentMonth() {
return employmentMonth;
}
public void setEmploymentMonth(int employmentMonth) {
this.employmentMonth = employmentMonth;
}
#Override
public String toString(){
return "Employee details: " + this.name + this.surname + this.address + this.employmentYear;
}
}

Filter an ArrayList via Optional in Java 8

I am getting an exception while trying to filter and iterate over a Optional using Java 8. I have an object Subject which is being added in an array list and a value of null also.
Problem Statement: I have an ArrayList, I want to iterate it, filter it and then based on that, only print that record which fulfills the condition.
package com.example.app;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class OptionalTest {
public static void main(String[] args) {
System.out.println("getSubject: " + getSubjects());
// print the Subject with the name "Math"
System.out.println("getSubject " + getSubjects().filter((Subject s) -> s.getName().equalsIgnoreCase("Math")));
}
private static Optional getSubjects() {
Subject subject1 = new Subject(1, "Math", (short)2, "");
Subject subject2 = new Subject(2, "Social Science", (short)4, "Social Science");
Subject subject3 = new Subject(3, "English", (short)6, "Literature");
List<Subject> subjects = new ArrayList<>();
Optional<List<Subject>> optional = Optional.of(subjects);
subjects.add(subject1);
subjects.add(subject2);
subjects.add(null);
subjects.add(subject3);
return optional;
}
}
class Subject {
int id;
String name;
short type;
String description;
public Subject(int id, String name, short type, String description) {
this.id = id;
this.name = name;
this.type = type;
this.description = description;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public short getType() {
return type;
}
public void setType(short type) {
this.type = type;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Override
public String toString() {
return "\nSubject{" + "id=" + id + ", name=" + name + ", type=" + type + ", description=" + description + '}'+"\n";
}
}
Using Optional.filter would be used to filter List instances as per your code snippet. This is not what you want:
Optional.of(getSubjects()).filter(predicate) //filters lists, not subjects in lists
Your intention is probably to use the a list of Subject objects, then filter. It's the filter method of the Stream interface that returns an Optional instance:
I'd change this method:
private static List<Subject> getSubjects(){
Subject subject1 = new Subject(1, "Math", (short)2, "");
Subject subject2 = new Subject(2, "Social Science", (short)4, "Social Science");
Subject subject3 = new Subject(3, "English", (short)6, "Literature");
List<Subject> subjects = new ArrayList<>();
subjects.add(subject1);
subjects.add(subject2);
subjects.add(null);
subjects.add(subject3);
return subjects;
}
And then use it as follows:
Optional<Subject> filtered = getSubjects()
.stream().filter(s -> s.getName().equalsIgnoreCase("Math"))
//Find first is just one of the many Stream methods
//returning an optional
//It's correct to use it in this case because you know
//only one value is expected to match the filter predicate.
.findFirst();
In fact, if you expect more than one subject to match your filter, you should collect, instead, instead of picking one. In this case, you don't need an optional:
List<Subject> mathSubjects = getSubjects()
.stream().filter((s -> s.getName().equalsIgnoreCase("Math")))
.collect(Collectors.toList());
You can do it very simply using lambda expression, I am providing you a sample so that you can modify according to your need.
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class Java8Optional {
public static void main(String[] args) {
List<Employee> employeeList = new ArrayList<>();
employeeList.add(new Employee(1, "syed"));
employeeList.add(new Employee(2, "az"));
employeeList.add(null);
employeeList.add(new Employee(4, "Rof"));
employeeList.forEach(n -> Optional.ofNullable(n).ifPresent(e -> System.out.println("Employee ID="+e.employeeId+"\tEmployee Name="+e.employeeName)));
}
static class Employee {
Integer employeeId;
String employeeName;
public Integer getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public Employee(Integer employeeId, String employeeName) {
super();
this.employeeId = employeeId;
this.employeeName = employeeName;
}
}
}

Categories