DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Jackson vs Gson: Edge Cases in JSON Parsing for Java Apps
  • Is It Okay To Stop Running Your Tests After the First Failure?
  • What Is API-First?
  • Testcontainers With Kotlin and Spring Data R2DBC

Trending

  • Rethinking Recruitment: A Journey Through Hiring Practices
  • Segmentation Violation and How Rust Helps Overcome It
  • Doris: Unifying SQL Dialects for a Seamless Data Query Ecosystem
  • Unlocking AI Coding Assistants Part 1: Real-World Use Cases
  1. DZone
  2. Coding
  3. Languages
  4. Jackson Annotations for JSON (Part 4): General Annotations

Jackson Annotations for JSON (Part 4): General Annotations

In this final series post about Jackson annotations, let's look at some general use annotations that are handy to have at the ready.

By 
John Thompson user avatar
John Thompson
·
Dec. 26, 17 · Tutorial
Likes (10)
Comment
Save
Tweet
Share
115.9K Views

Join the DZone community and get the full member experience.

Join For Free

Jackson is a suite of data-processing tools for Java comprising of three components:

  • Streaming (jackson-core) defines low-level streaming APIs and includes JSON-specific implementations.

  • Annotations (jackson-annotations) contains standard Jackson annotations.

  • Databind (jackson-databind) implements data-binding (and object serialization) support on the streaming package. This package depends on both the streaming and annotations packages.

In this series of articles, I will explain data binding Java objects to JSON using Jackson annotations. I will take up each of the Jackson annotations and explain, with code snippets, how to use them. Each annotation usage is accompanied with proper test cases.

If you want to catch up on what's happened so far, read:

  • Part 1: Serialization and Deserialization

  • Part 2: Serialization

  • Part 3: Deserialization

General Annotations

The general annotations are:

  • @JsonProperty
  • @JsonFormat
  • @JsonUnwrapped
  • @JsonView
  • @JsonManagedReference and @JsonBackReference
  • @JsonIdentityInfo
  • @JsonFilter

@JsonProperty

The @JsonProperty annotation is used to map property names with JSON keys during serialization and deserialization. By default, if you try to serialize a POJO, the generated JSON will have keys mapped to the fields of the POJO. If you want to override this behavior, you can use the @JsonProperty annotation on the fields. It takes a String attribute that specifies the name that should be mapped to the field during serialization.

You can also use this annotation during deserialization when the property names of the JSON and the field names of the Java object do not match.

Let us consider an example Java class that uses the @JsonProperty annotation.

PropertyDemoBean.java

package guru.springframework.blog.jsonannotation.domain.general;

import com.fasterxml.jackson.annotation.JsonProperty;

public class PropertyDemoBean {
    @JsonProperty("person-id")
    public long personId = 123L;
    @JsonProperty("name")
    public String  name = "James Clark";

    @Override
    public String toString() {
        return "PropertyDemoBean{" +
                "personId=" + personId +
                ", name='" + name + '\'' +
                '}';
    }
}


The test code to test the @JsonProperty annotation is:

@Test
public void testSerializingWithJsonProperty()
throws JsonProcessingException {
    String jsonString = objectMapper.writeValueAsString(new PropertyDemoBean());
    System.out.println(jsonString);
    assertThat(jsonString, containsString("James Clark"));
    assertThat(jsonString, containsString("123"));
}
@Test
public void testDeSerializingWithJsonProperty() throws IOException {
    String jsonString = "{\"person-id\": 231, \"name\": \"Mary Parker\"}";
    ObjectMapper mapper = new ObjectMapper();
    PropertyDemoBean bean = objectMapper.readValue(jsonString, PropertyDemoBean.class);
    System.out.println(bean);
    assertThat(bean.name, is(equalTo("Mary Parker")));
    assertThat(bean.personId, is(equalTo(231 L)));
}


The output of running the test in IntelliJ is:
@JsonProperty Test Output

@JsonFormat

The @JsonFormat annotation is used to tell Jackson that the format in which the value for a field is serialized. It specifies the format using the JsonFormat.Shape enum.

Let us consider an example Java class that uses the @JsonFormat annotation to modify the Date and Time format of an activeDate field.

FormatDemoBean.java

package guru.springframework.blog.jsonannotation.domain.general;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.Date;

public class FormatDemoBean {
    @JsonProperty("person-id")
    public long personId = 123L;
    @JsonProperty("name")
    public String  name = "James Clark";
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
    @JsonProperty("active-date")
    public Date activeDate;

    public FormatDemoBean() {
    }
    public void setActiveDate(Date activeDate) {
        this.activeDate = activeDate;
    }
}


The test code to test the @JsonFormat annotation is:

@Test
public void testSerializingWithJsonFormat()
    throws JsonProcessingException, ParseException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        String dateAndTime = "26-09-2017 11:00:00";
        Date date = simpleDateFormat.parse(dateAndTime);
        FormatDemoBean fb = new FormatDemoBean();
        fb.setActiveDate(date);
        String jsonString = objectMapper.writeValueAsString(fb);
        System.out.println(jsonString);
        assertThat(jsonString, containsString("James Clark"));
        assertThat(jsonString, containsString("123"));
        assertThat(jsonString, containsString("26-09-2017 11:00:00"));
}


The output of running the test in IntelliJ is:
@JsonFormat Test Output

@JsonUnwrapped

The @JsonUnwrapped annotation unwraps the values during serialization and deserialization. It helps in rendering the values of a composed class as if they belonged to the parent class. Let us consider an example of Java class that uses the @JsonUnwrapped annotation.

UnwrappedDemoBean.java

package guru.springframework.blog.jsonannotation.domain.general;

import com.fasterxml.jackson.annotation.JsonUnwrapped;

public class UnwrappedDemoBean {
    public static class Address {
        public String doorNumber = "12";
        public String streetName = "phase-1";
        public String pinCode = "123456";
        public String city = "New York";

        @Override
        public String toString() {
            return "Address{" +
                    "doorNumber='" + doorNumber + '\'' +
                    ", streetName='" + streetName + '\'' +
                    ", pinCode='" + pinCode + '\'' +
                    ", city='" + city + '\'' +
                    '}';
        }
    }
    public long personId = 0;
    public String  name = "James Clark";
    @JsonUnwrapped
    public Address address = new Address();
}


In this example, the Address class is inside the UnwrappedDemoBean class. Without the @JsonUnwrapped annotation, the serialized Java object would be similar to this:

{
    "personId": 0,
    "name": "James Clark",
    "address": {
        "doorNumber": "12",
        "streetName": "phase-1",
        "pinCode": "123456",
        "city": "New York"
    }
}


Let us see what happens when you use the @JsonUnwrapped annotation.

The test code to test the @JsonUnwrapped annotation is:

@Test
public void testSerializingWithJsonUnwrapped()
    throws JsonProcessingException {
        String jsonString = objectMapper.writeValueAsString(new UnwrappedDemoBean());
        System.out.println(jsonString);
        assertThat(jsonString, containsString("James Clark"));
        assertThat(jsonString, not(containsString("address")));
}


The output of running the test in IntelliJ is:
@JsonUnwrapped Test Output

As you can see, the Address object is unwrapped and is displayed as the properties of the parent classUnwrappedDemoBean.

@JsonView

The @JsonView annotation is used to include or exclude a property dynamically during serialization and deserialization. It tells the view in which the properties are rendered. Let us consider an example Java class that uses the @JsonView annotation with Public and Internal views.

ViewDemoBean.java

package guru.springframework.blog.jsonannotation.domain.general;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonView;

public class ViewDemoBean {
    @JsonView(Views.Public.class)
    @JsonProperty
    public long personId = 0;
    @JsonView(Views.Public.class)
    @JsonProperty
    public String  name = "James Clark";
    @JsonView(Views.Internal.class)
    @JsonProperty
    public String gender = "male";

    @Override
    public String toString() {
        return "ViewDemoBean{" +
                "personId=" + personId +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                '}';
    }
}


The test code to test the @JsonView annotation is:

@Test
public void testSerializingWithJsonView()
    throws JsonProcessingException {
        String jsonString = objectMapper.writerWithView(Views.Public.class)
            .writeValueAsString(new ViewDemoBean());
        String jsonStringInternal = objectMapper.writerWithView(Views.Internal.class)
            .writeValueAsString(new ViewDemoBean());
        System.out.println(jsonString);
        System.out.println(jsonStringInternal);
        assertThat(jsonString, containsString("James Clark"));
        assertThat(jsonString, not(containsString("gender")));
        assertThat(jsonStringInternal, containsString("gender"));
}


As you can see in the test code, you need to configure the ObjectMapper to include which type of view must be used for writing the JSON from the Java object using the writerWithView() method.

The output of running the test in IntelliJ is:

When the JSON is generated in the public view, only personId and name fields are serialized omitting the gender field. But when the JSON is generated in the internal view all the fields are serialized.

@JsonManagedReference and @JsonBackReference

The @JsonManagedReference and @JsonBackReference annotation are used to create JSON structures that have a bidirectional relationship. Without this annotation, you get an error like this:

"com.fasterxml.jackson.databind.JsonMappingException:Infinite recursion (StackOverflowError)"


Let us consider an example Java class that uses the @JsonManagedReference and @JsonBackReference annotations:

ManagedReferenceDemoBean.java

package guru.springframework.blog.jsonannotation.domain.general;

import com.fasterxml.jackson.annotation.JsonManagedReference;

public class ManagedReferenceDemoBean {
    public long personId = 0;
    public String  name = "James Clark";
    @JsonManagedReference
    public BackReferenceDemoBean manager;

    public ManagedReferenceDemoBean(long personId, String name, BackReferenceDemoBean manager) {
        this.personId = personId;
        this.name = name;
        this.manager = manager;
    }
}


BackReferenceDemoBean.java

package guru.springframework.blog.jsonannotation.domain.general;

import com.fasterxml.jackson.annotation.JsonBackReference;

import java.util.ArrayList;
import java.util.List;

public class BackReferenceDemoBean {
    public long personId = 123;
    public String  name = "John Thomas";
    @JsonBackReference
    public List<ManagedReferenceDemoBean> employees;

    public BackReferenceDemoBean(long personId, String name) {
        this.personId = personId;
        this.name = name;
        employees = new ArrayList<ManagedReferenceDemoBean>();
    }

    public void addEmployees(ManagedReferenceDemoBean managedReferenceDemoBean){
        employees.add(managedReferenceDemoBean);
    }
}


The test code to test both @JsonManagedReference and @JsonBackReference annotations is:

@Test
public void testSerializingWithJsonManagedAndBackReference()
    throws JsonProcessingException {
        BackReferenceDemoBean demoBean = new BackReferenceDemoBean(123 L, "Mary Parker");
        ManagedReferenceDemoBean bean = new ManagedReferenceDemoBean(231 L, "John Thomas", demoBean);
        demoBean.addEmployees(bean);
        String jsonString = objectMapper.writeValueAsString(bean);
        System.out.println(jsonString);
        assertThat(jsonString, containsString("John Thomas"));
        assertThat(jsonString, containsString("231"));
        assertThat(jsonString, not(containsString("employees")));
}


The output of running the test in IntelliJ is:
@BackReferenceDemoBean and @ManagedReferenceDemoBean Test Output

As you can see, the field marked with @JsonManagedReference is the forward reference which will be included during serialization. The field marked with @JsonBackReference is the back reference and is usually omitted during serialization.

@JsonIdentityInfo

The @JsonIdentityInfo tells Jackson to perform serialization or deserialization using the identity of the object. This annotation works similar to the @JsonManagedReference and @JsonBackReference annotations with the difference that @JsonIdentityInfo includes the back reference object.

Let us consider an example where the IdentityInfoEmployeeDemoBean has a bidirectional relationship withIdentityInfoManagerDemoBean using the @JsonIdentityInfo annotation.

IdentityInfoEmployeeDemoBean.java

package guru.springframework.blog.jsonannotation.domain.general;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "personId")
public class IdentityInfoEmployeeDemoBean {
    public long personId = 0;
    public String  name = "James Clark";
    public IdentityInfoManagerDemoBean manager;

    public IdentityInfoEmployeeDemoBean(long personId, String name, IdentityInfoManagerDemoBean manager) {
        this.personId = personId;
        this.name = name;
        this.manager = manager;
    }
}


IdentityInfoManagerDemoBean.java

package guru.springframework.blog.jsonannotation.domain.general;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

import java.util.ArrayList;
import java.util.List;

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "personId")
public class IdentityInfoManagerDemoBean {
    public long personId = 123;
    public String  name = "John Thomas";
    public List<IdentityInfoEmployeeDemoBean> employees;

    public IdentityInfoManagerDemoBean(long personId, String name) {
        this.personId = personId;
        this.name = name;
        employees = new ArrayList<IdentityInfoEmployeeDemoBean>();
    }

    public void addEmployees(IdentityInfoEmployeeDemoBean identityInfoEmployeeDemoBean){
        employees.add(identityInfoEmployeeDemoBean);
    }
}


The test code to test the @JsonIdentityInfo annotation is:

@Test
public void testSerializingWithJsonIdentityInfo()
    throws JsonProcessingException {
        IdentityInfoManagerDemoBean demoBean = new IdentityInfoManagerDemoBean(123 L, "Mary Parker");
        IdentityInfoEmployeeDemoBean bean = new IdentityInfoEmployeeDemoBean(231 L, "John Thomas", demoBean);
        demoBean.addEmployees(bean);
        String jsonString = objectMapper.writeValueAsString(bean);
        System.out.println(jsonString);
        assertThat(jsonString, containsString("John Thomas"));
        assertThat(jsonString, containsString("231"));
        assertThat(jsonString, containsString("employees"));
}


The output of running the test in IntelliJ is:
@JsonIdentityInfo Test Output

As you can see, the output gives the information about the employee with his manager details. It also provides the additional information about the employees under the manager.

@JsonFilter

The @JsonFilter annotation is used to tell Jackson to use a custom defined filter to serialize the Java object. To define your filter, you need to use the FilterProvider class. This provider gets the actual filter instance to use. The filter is then configured by assigning the FilterProvider to ObjectMapper.

Let us consider an example of Java class that uses the @JsonFilter annotation.

FilterDemoBean.java

package guru.springframework.blog.jsonannotation.domain.general;

import com.fasterxml.jackson.annotation.JsonFilter;

@JsonFilter("customFilter")
public class FilterDemoBean {
    public long personId = 123L;
    public String  name = "James Clark";
    public String gender = "male";

}


The test code to test the @JsonFilter annotation is:

@Test
public void testSerializingWithJsonFilter()
    throws JsonProcessingException {
        FilterProvider filterProvider = new SimpleFilterProvider().
        addFilter("customFilter",
            SimpleBeanPropertyFilter.filterOutAllExcept("name"));
        String jsonString = objectMapper.writer(filterProvider).
        writeValueAsString(new FilterDemoBean());
        System.out.println(jsonString);
        assertThat(jsonString, containsString("James Clark"));
        assertThat(jsonString, not(containsString("123")));
}


The output of running the test in IntelliJ is:

@JsonFilter Test Output
As you can see, the custom filter declared as the arguments of the @JsonFilter annotation extract only the name and filters out the other properties of the bean during serialization.

You can download the source code of this post from here.

Annotation JSON Jackson (API) Testing

Published at DZone with permission of John Thompson, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Jackson vs Gson: Edge Cases in JSON Parsing for Java Apps
  • Is It Okay To Stop Running Your Tests After the First Failure?
  • What Is API-First?
  • Testcontainers With Kotlin and Spring Data R2DBC

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!