Java: Stream.Reduce

In this post, we are going to see how we can use Stream reduce operation on a list with an example.

Stream.Reduce()

Stream.Reduce() is a general purpose reduction operation. We can use this operation to process a list and find out the max, min and average value. It’s like applying to a list of values and get a single result value.

The syntax is given below


T reduce(T identity, BinaryOperator accumulator)

Here identity is the initial value and accumulator is a function for combining values.

reduce

In the above example, I have a list of string and want to find out the long string in it. I have used reduce operation to find it out.

Let’s see another example. Assume that we want to process a list and modify the value of it and then reduce the value into a single value. Refer the below example to know how we can achieve this with reduce operation.

reduce-modify-list

Advertisements

Dozer Mapping

Dozer is a Java Bean to Java Bean mapper that recursively copies data from one object to another. Typically, these Java Beans will be of different complex types.

This mapper helps us to avoid unnecessary mapping code in our application. Internally it uses reflection.

In this post, We are going to see how we can use this with an example.

Assume that we have a “Person” Bean class which contains the first name, last name, age and address fields. We want to map it into another bean class PersonVO. In addition to that, we want to map the name details into NameVO and address details into AddressVo. Let’s look at below to know how we can do that with Dozer.

Here is our Person Bean class.

Person.java


package com.dozerExample;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

public class Person {

    private String id;

    private String firstName;

    private String lastName;

    private int age;

    private String streetAddress;

    private String city;

    private String state;

    private String zip;

    private String phoneNumber;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    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;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getStreetAddress() {
        return streetAddress;
    }

    public void setStreetAddress(String streetAddress) {
        this.streetAddress = streetAddress;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getZip() {
        return zip;
    }

    public void setZip(String zip) {
        this.zip = zip;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);
    }

    @Override
    public boolean equals(Object obj) {
        return EqualsBuilder.reflectionEquals(this, obj);
    }
}

We want to map the Person Bean into PersonVO bean and also the first name and last name fields are mapped to NameVO and address are mapped to AddressVO.

Here is the PersonVO Bean class contains the person id, age and NameVo and AddressVo fields.

PersonVO


package com.dozerExample;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

public class PersonVO {

    private String id;

    private int age;

    private NameVO nameVO;

    private AddressVO addressVO;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public NameVO getNameVO() {
        return nameVO;
    }

    public void setNameVO(NameVO nameVO) {
        this.nameVO = nameVO;
    }

    public AddressVO getAddressVO() {
        return addressVO;
    }

    public void setAddressVO(AddressVO addressVO) {
        this.addressVO = addressVO;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);
    }

    @Override
    public boolean equals(Object obj) {
        return EqualsBuilder.reflectionEquals(this, obj);
    }
}


Here is the NameVO Bean contains the first and last name fields.
NameVO


package com.dozerExample;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

public class NameVO {

    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;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);
    }

    @Override
    public boolean equals(Object obj) {
        return EqualsBuilder.reflectionEquals(this, obj);
    }

}

Here is the AddressVO Bean contains the stress address, city, state, zip and phone number fields.
AddressVO


package com.dozerExample;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

public class AddressVO {

    private String streetAddress;

    private String city;

    private String state;

    private String zip;

    private String phoneNumber;

    public String getStreetAddress() {
        return streetAddress;
    }

    public void setStreetAddress(String streetAddress) {
        this.streetAddress = streetAddress;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getZip() {
        return zip;
    }

    public void setZip(String zip) {
        this.zip = zip;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);
    }

    @Override
    public boolean equals(Object obj) {
        return EqualsBuilder.reflectionEquals(this, obj);
    }
}

We have to include the below dependency in your pom.xml file.
pom.xml


<dependency>
            <groupId>com.github.dozermapper</groupId>
            <artifactId>dozer-core</artifactId>
            <version>6.1.0</version>
 </dependency>

Then, we need to define the bean mapping field details in an XML. Here is the dozer mapping XML.


<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozermapper.github.io/schema/bean-mapping"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozermapper.github.io/schema/bean-mapping http://dozermapper.github.io/schema/bean-mapping.xsd">
    <mapping wildcard="true" map-null="false">
        <class-a>com.dozerExample.Person</class-a>
        <class-b>com.dozerExample.PersonVO</class-b>
        <field>
            <a>firstName</a>
            <b>nameVo.firstName</b>
        </field>
        <field>
            <a>lastName</a>
            <b>nameVo.lastName</b>
        </field>
        <field>
            <a>streetAddress</a>
            <b>addressVo.streetAddress</b>
        </field>
        <field>
            <a>city</a>
            <b>addressVo.city</b>
        </field>
        <field>
            <a>state</a>
            <b>addressVo.state</b>
        </field>
        <field>
            <a>zip</a>
            <b>addressVo.zip</b>
        </field>
        <field>
            <a>phoneNumber</a>
            <b>addressVo.phoneNumber</b>
        </field>
    </mapping>
</mappings>

Now, Let’s use the above mapping file and convert Person into PersonVO and vice versa.



package com.dozerExample;

import org.dozer.DozerBeanMapperBuilder;
import org.dozer.Mapper;

import java.util.UUID;

public class DozerApp {

    public static final String PERSON_DOZER_MAPPING_XML = "dozer-mapping.xml";

    private static Mapper mapper = DozerBeanMapperBuilder.create().withMappingFiles(PERSON_DOZER_MAPPING_XML).build();;

    public static void main(String[] args) {
        Person person = new Person();
        person.setId(UUID.randomUUID().toString());
        person.setFirstName("Michael");
        person.setLastName("Keller");
        person.setAge(45);
        person.setStreetAddress("102 walker st");
        person.setCity("Delaware");
        person.setState("OH");
        person.setZip("45454");
        person.setPhoneNumber("614-434-4535");
        PersonVO personVO = mapper.map(person, PersonVO.class);
        System.out.println("Converting from person to personVO" + personVO);
        Person newPerson = mapper.map(personVO, Person.class);
        System.out.println("Converting from personVO to person" + newPerson);
    }
}

The following is the output of the above program.


Converting from person to personVOcom.dozerExample.PersonVO@467aecef[id=bd993f0e-48f6-4bf7-a942-f902c156ffd4,age=45,nameVO=com.dozerExample.NameVO@5bfbf16f[firstName=Michael,lastName=Keller],addressVO=com.dozerExample.AddressVO@25af5db5[streetAddress=102 walker st,city=Delaware,state=OH,zip=45454,phoneNumber=614-434-4535]]
Converting from personVO to personcom.dozerExample.Person@5f3a4b84[id=bd993f0e-48f6-4bf7-a942-f902c156ffd4,firstName=Michael,lastName=Keller,age=45,streetAddress=102 walker st,city=Delaware,state=OH,zip=45454,phoneNumber=614-434-4535]

JUnit4 Exception testing

In this post, I am going to show how we can write a JUnit test case to test a method which throws an exception.

We can follow any one of the below approaches to test the method throws an exception. Here, I am using JUnit 4.

1. @Test annotation with “expected” attribute
2. try-catch and fail()
3. ExpectedException

Here is my class and contains a method “validateUserData” which has logic to validate the userName and name. If anyone of this has either null or blank then it will throw a RuntimeException with the appropriate message.


import org.apache.commons.lang3.StringUtils;

public class UserDataValidator {

    public void validateUserData(String userName, String name){
        if(StringUtils.isBlank(userName)){
            throw new RuntimeException("userName is null or blank");
        }
        if(StringUtils.isBlank(name)){
            throw new RuntimeException("name is null or blank");
        }
    }
}

Let’s write a JUnit test case to test this method with the above approaches.

@Test annotation with “expected” attribute

Here is the JUnit test to test the “validateUserData” method. I am passing the “userName” as null. So as expected the method throws a RuntimeException.


import org.junit.Test;

public class UserDataValidatorTest {

    @Test(expected = RuntimeException.class)
    public void testValidateUserData_userNameIsNull(){
        UserDataValidator userDataValidator = new UserDataValidator();
        userDataValidator.validateUserData(null, "test");
    }
}

try-catch and fail()

if we want to assert the exception message, then we can follow this approach.


import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

public class UserDataValidatorTest {
    
    @Test
    public void testValidateUserData_userNameIsNull(){
        UserDataValidator userDataValidator = new UserDataValidator();
        try{
            userDataValidator.validateUserData(null, "test");
            fail();
        }catch (Exception e){
            assertEquals("userName is null or blank", e.getMessage());
        }

    }
}

ExpectedException

The ExpectedException rule is used to test the exception type and also the exception message.


import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

public class UserDataValidatorTest {

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void testValidateUserData_userNameIsNull(){
        thrown.expect(RuntimeException.class);
        thrown.expectMessage("userName is null or blank");
        UserDataValidator userDataValidator = new UserDataValidator();
        userDataValidator.validateUserData(null, "test");
    }
}

Java 8 Lambda – Using Map

In this post, I am going to show some examples of using Java 8 Stream map().

I have an arraylist which contains list of strings and want to change the contents of that list to upper case. We can easily do that with map() but I want to make sure that my code should work fine for all types of input such as valid list, empty list and null list.

Refer the below code to know how to do that.


import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class LambdaExample {

    public static void main(String[] args) {

        //Passing valid list
        List validList = Arrays.asList("Name1", "Name2");
        System.out.println("Converting valid list contents to upper case: " + convertAllToUpperCase(validList));
        //Passing an empty list
        System.out.println("Converting an empty list  to upper case: " + convertAllToUpperCase(Collections.emptyList()));
        //Passing null
        System.out.println("Converting null list to upper case: " + convertAllToUpperCase(null));
    }

    private static List convertAllToUpperCase(List list) {
        return Optional.ofNullable(list)
                .orElseGet(Collections::emptyList).stream().map(String::toUpperCase)
                .collect(Collectors.toList());
    }

}


Here, I have used the “Optional” class to handle null list. The code “Optional.ofNullable(list).orElseGet(Collections::emptyList)” will return an empty list when the list is null.

The output of the above program will look like below,


Converting valid list contents to upper case: [NAME1, NAME2]
Converting an empty list  to upper case: []
Converting null list to upper case: []

Now lets see an another example to show how we can map one model class to an another class via Java 8 Functional interface.

There are two model classes here. They are Person and PersonBean. So I want to iterate through the Person list and create an another list of PersonBean.

I have used Function inferface which represents a function that accepts one argument and produces a result. Here the argument is Person and the result is PersonBean.

The first step is to define the function and then use that function while mapping. Refer the below code to know how to do that.


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

public class LambdaExample {

    Function personBeanFunction = person -> {
        PersonBean personBean = new PersonBean();
        personBean.age = person.age;
        personBean.firstName = person.givenName;
        personBean.lastName = person.familyName;
        return personBean;
    };

    public static void main(String[] args) {
        LambdaExample lambdaExample = new LambdaExample();
        List personList = new ArrayList();
        Person person = new Person();
        person.age = 45;
        person.familyName = "test";
        person.givenName = "test";
        personList.add(person);
        System.out.println("PersonList list:" + personList);
        System.out.println("Person Bean list:" + lambdaExample.convert(personList));
    }

    private List convert(List list) {
        return Optional.ofNullable(list).orElseGet(Collections::emptyList).stream()
                .map(personBeanFunction).collect(Collectors.toList());
    }

    private static class Person {
        String familyName;
        String givenName;
        int age;

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("Person{");
            sb.append("familyName='").append(familyName).append('\'');
            sb.append(", givenName='").append(givenName).append('\'');
            sb.append(", age=").append(age);
            sb.append('}');
            return sb.toString();
        }
    }

    private static class PersonBean {
        String firstName;
        String lastName;
        int age;

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("PersonBean{");
            sb.append("firstName='").append(firstName).append('\'');
            sb.append(", lastName='").append(lastName).append('\'');
            sb.append(", age=").append(age);
            sb.append('}');
            return sb.toString();
        }
    }
}

The output of the above program will look like below,


PersonList list:[Person{familyName='test', givenName='test', age=45}]
Person Bean list:[PersonBean{firstName='test', lastName='test', age=45}]

HK2 injection in Standalone Java Application with Custom Binder

In my previous post, I gave an example of using the HK2 injection in a stand-alone application.

Please refer it in the below link.
Hk2 Injection with hk2 inhabitant file

In the above example, we have to create an HK2 inhabitant file which contains the binding information such as contract and service name and their mapping. The overhead in this approach is generating the HK2 default file. We can automate the file creation process in the maven build with hk2-inhabitant-generator plugin. But at some point, we may want to define the mapping explicitly such as using the service and contract from different libs. The hk2-inhabitant-generator may not add the contract/service mapping in the inhabitant file. Therefore, to resolve the above problem, we need to define a custom binder which contains all the bindings details.

Let’s see how can use the HK2 injection in a stand-alone application with a custom binder.

The below one is my POM file.
hk2-injection-pom

We have a standalone application which will be used for adding, deleting, updating the user details into HashMap or Google Guava Cache. We have two interfaces(UserService for service and UserDao for DAO) and three implementation classes(UserServiceImpl, UserGuavaCacheImpl and UserLocalCacheDaoImpl)

UserGuavaCacheImpl is used to do CRUD operation on Guava Chache and UserLocalCacheDaoImpl is used to do CRUD operation on HashMap.

We decide to use either Local cache or Google Guava cache based on a system property. This is a simple example to show how we can use the HK2 injection.
Refer the below image to know my project structure.

hk2-project-structure

All our interfaces should be annotated with @Contract and all the implementation classes should be annotated with @Service.

Refer below my Service implementation.
hk2-userservice

Refer my various DAO implementations below.

Refer my custom binder file which has the binding information below.

hk2-dependencybinder

In the above file, I have used “Named” annotation as we have two services for UserDao interface. If we don’t provide that then by default, the service locator injects the first available implementation.

Below is my main class which invokes the User Service to perform various CRUD operations with User Object.
hk2-Application
The below code is used to create a ServiceLocator instance and then we bind the created service locator with our custom binder.


ServiceLocator serviceLocator = ServiceLocatorFactory.getInstance().create("serviceLocator");
ServiceLocatorUtilities.bind(serviceLocator, new DependencyBinder());

We can use the ServiceLocator object for getting the Object instance like below


UserService userService = serviceLocator.getService(UserService.class, "empService");

We have two DAO implementation. We need to decide which one to use. The hk2 IterableProvider will give both the implementations classes. Therefore, I have used a system property to decide the appropriate cache implementation. Refer the below code from UserServiceImpl.


@Inject
    public UserServiceImpl(IterableProvider iterableProviders) {
        String cache = System.getProperty("CACHE");
        if (CacheDetails.GUAVA.name().equals(cache)) {
            this.userDao = iterableProviders.named("empGuavaCacheDao").get();
        }
        else if(CacheDetails.LOCAL.name().equals(cache)) {
            this.userDao = iterableProviders.named("empLocalCacheDao").get();
        }
    }

 
The output of my program is as given below,


Add user details
The user details after it has been added User: User{id='0701f22b-10b0-4d6f-8c8d-410da89646f9', firstName='First Name', lastName='Last Name}
Now fetch the user details with ID
User :User{id='0701f22b-10b0-4d6f-8c8d-410da89646f9', firstName='First Name', lastName='Last Name}
Now update the user details
User After Updation:User{id='0701f22b-10b0-4d6f-8c8d-410da89646f9', firstName='Bala', lastName='Samy}
Now delete the user details
User After Deletion:null


Testing HK2 injection
Fetching the various DAO implementation
UserDao userDao = serviceLocator.getService(UserDao.class, "empGuavaCacheDao")
userDao instanceof UserGuavaCacheImpl - > Its an instance of UserGuavaCacheImpl 
userDao = serviceLocator.getService(UserDao.class, "empLocalCacheDao")
userDao instanceof UserLocalCacheDaoImpl - > Its an instance of UserLocalCacheDaoImpl 


 

Refer the code @hk2-java-custombinder

 

 

How to use Maven Shade Plugin

Maven Shade Plugin provides the capability to package the artifact in an uber-jar, including its dependencies and to shade(rename) the packages of some of the dependencies.

This plugin is used to take all the dependencies and extract the content and put them as is and create one big JAR(Uber. This will be good for runnable jar but if you are planning to use this jar as a maven dependency then there may be some conflicts with the client jar. So we need to use the relocations option to relocate the conflicting classes to a different package.

In this post, I will show how to use this plugin with an example.

The below example is for creating the final Uber jar without relocations.

Create Uber Jar without relocations

Consider that we have a web app which exposes a REST web service and running on a grizzly container. Now, let’s use the shade plugin to create the fat jar.

I am not going to give the code examples here as this post explains about the shade plugin only.

Refer the complete code https://github.com/dkbalachandar/greeting-rest

The below plugin configuration has to be added in the maven pom file.

Refer the configuration section where we have to specify various options. They are given below,

  • mainclass – The entry point of the application
  • createSourcesJar – Create the sources jar as well
  • shadedClassifierName – The name of the classifier used in case the shaded artifact is attached
  • shadedArtifactAttached – It defines whether the shaded artifact should be attached as classifier to the original artifact. If its false, then the original jar will be replaced by the shaded jar

    <plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-shade-plugin</artifactId>
	<version>3.1.0</version>
	<executions>
		<execution>
			<phase>package</phase>
			<goals>
				<goal>shade</goal>
			</goals>
			<configuration>
				<createSourcesJar>true</createSourcesJar>
				<shadedClassifierName>shaded</shadedClassifierName>
				<shadedArtifactAttached>true</shadedArtifactAttached>
				<transformers>
					<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
						<mainClass>com.Grizzly</mainClass>
					</transformer>
				</transformers>
				<filters>
					<filter>
						<artifact>*:*</artifact>
						<excludes>
							<exclude>META-INF/*.SF</exclude>
							<exclude>META-INF/*.DSA</exclude>
							<exclude>META-INF/*.RSA</exclude>
						</excludes>
					</filter>
				</filters>                          
			</configuration>
		</execution>
	</executions>
</plugin>

Then execute ‘mvn package’ command and then check the target folder. You will be able to see the shaded jar and it ends with shaded suffix. Refer the below screenshot.

shaded-jar

When you extract out the contents of the shaded jar, you will see below.

shaded-jar-extraction

Create Uber Jar with relocations

Now we are going to see how we can relocate some of the classes inside the Uber Jar. Lets use the same example and add the below relocations inside the configuration element.


  <configuration>
	<createSourcesJar>true</createSourcesJar>
	<shadedClassifierName>shaded</shadedClassifierName>
	<shadedArtifactAttached>true</shadedArtifactAttached>
	<transformers>
		<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
			<mainClass>com.Grizzly</mainClass>
		</transformer>
	</transformers>
	<filters>
		<filter>
			<artifact>*:*</artifact>
			<excludes>
				<exclude>META-INF/*.SF</exclude>
				<exclude>META-INF/*.DSA</exclude>
				<exclude>META-INF/*.RSA</exclude>
			</excludes>
		</filter>
	</filters>
	<relocations>
		<relocation>
			<pattern>org.glassfish.grizzly.utils</pattern>
			<shadedPattern>org.shaded.glassfish.grizzly.utils</shadedPattern>
			<excludes>
				<exclude>org.glassfish.grizzly.utils.conditions.*</exclude>
			</excludes>
		</relocation>
	</relocations>
</configuration>

Check the element and the child elements. Here, I am planning to rename/move the org.glassfish.grizzly.utils package to org.shaded.glassfish.grizzly.utils and exclude the package org.glassfish.grizzly.utils.conditions.

Then run the mvn package again to create the shaded jar and extract out the contents of the shaded jar. It will look like below.

shaded-jar-relocations

As you can see that the package org.glassfish.grizzly.utils has been moved to org.shaded.glassfish.grizzly.utils but the inner package org.glassfish.grizzly.utils.conditions has been kept it as in the oringal location.

While relocating the packages, this plugin will also update the affected bytecode. So you will not see any issues while running the Uber jar.

Exclude fields from Serialization with GSON API

GSON is a Java serialization/deserialization library to convert Java Objects into JSON and back.

In this post, we are going to see how we can exclude some fields from serialization with the GSON API.

Refer the below model class.

Employee.java



import org.apache.commons.lang3.builder.ToStringBuilder;

public class Employee {

    private String firstName;
    private String lastName;
    private int salary;

    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;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
}

The above Employee class has three fields such as first name, last name and salary. Consider that we want to exclude the salary field while serializing the employee object.

To do so, then we have to create a class which implements the ExclusionStrategy interface and provide our logic over there.

SalaryExclusionStrategy.java


import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;

public class SalaryExclusionStrategy implements ExclusionStrategy {

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return "salary".equalsIgnoreCase(f.getName());
    }

    @Override
    public boolean shouldSkipClass(Class clazz) {
        return false;
    }
}


We need to instruct the GSON parser to ignore the salary field. So while creating the GSON object, we have to pass an instance of SalaryExclusionStrategy which contains the logic to ignore the salary field to the addSerializationExclusionStrategy method.

Refer the below code to know how we can do that.


import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class GsonMain {

    private static final Gson GSON = new GsonBuilder()
            .setFieldNamingStrategy(FieldNamingPolicy.IDENTITY)
            .setPrettyPrinting()
            .addSerializationExclusionStrategy(new SalaryExclusionStrategy())
            .create();

    public static void main(String[] args) {
        Employee employee = new Employee();
        employee.setFirstName("Bala");
        employee.setLastName("Samy");
        System.out.println(GSON.toJson(employee));
    }
}


Refer below the output.


{
  "firstName": "Bala",
  "lastName": "Samy"
}