Open In App

How to Handle Jackson Exceptions?

Last Updated : 05 Mar, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

JSON (JavaScript Object Notation) is the common way of communication medium from multiple API responses that helps to get the data. Data will be helpful to render in multiple web pages, android or ios apps. JSON comes with a standard and any text that contains a JSON object or JSON array has to satisfy the standard. In a maven java project, the below dependencies need to be used for availing Jackson.

XML




<dependencies>
        <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.5.0</version>
    </dependency>
    <dependency>
         <groupId>com.fasterxml.jackson.core</groupId>
         <artifactId>jackson-databind</artifactId>
         <version>2.5.0</version>
     </dependency>
      <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-annotations</artifactId>
          <version>2.5.0</version>
      </dependency>       
</dependencies>


While handling the JSON data using Jackson, exceptions also need to be handled gracefully. In this post, let us see how to handle it by seeing a sample java project. As it is a maven project, let us check with pom.xml. Above said dependencies are present in pom.xml.

Example Project

pom.xml

XML




<?xml version="1.0" encoding="UTF-8"?>
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                        http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <artifactId>jackson-exceptions</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>jackson-exceptions</name>    
    <groupId>com.gfg</groupId>
    <dependencies>
        <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.5.0</version>
    </dependency>
    <dependency>
         <groupId>com.fasterxml.jackson.core</groupId>
         <artifactId>jackson-databind</artifactId>
         <version>2.5.0</version>
     </dependency>
      <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-annotations</artifactId>
          <version>2.5.0</version>
      </dependency>      
      <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
</dependencies>
    <build>
        <finalName>jackson-exceptions</finalName>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
  
</project>


Let us see the src folder files first. They are mainly model files

AnimalLives.java

Java




public class AnimalLives {
    
    public PetAnimal animal;
  
    public AnimalLives() {
    }
}
  
abstract class PetAnimal {
    public String name;
  
    public PetAnimal() {
    }
}
  
class Cat extends PetAnimal {
    public int lives;
  
    public Cat() {
    }
}


AnimalLivesConfigured.java

Java




import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
  
public class AnimalLivesConfigured {
    public PetAnimalConfigured animal;
  
    public AnimalLivesConfigured() {
    }
}
  
@JsonDeserialize(as = CatConfigured.class)
abstract class PetAnimalConfigured {
    public String name;
  
    public PetAnimalConfigured() {
    }
}
  
class CatConfigured extends PetAnimalConfigured {
    public int lives;
  
    public CatConfigured() {
    }
}


In this file, as we mentioned @JsonDeserialize, there is no need to register it on the ObjectMapper.

Author.java

Java




public class Author {
    
    public int id;
    public String name;
  
    public Author() {
        super();
    }
  
    public Author(final int id, final String name) {
        this.id = id;
        this.name = name;
    }
  
    // API
    public int getId() {
        return id;
    }
  
    public String getName() {
        return name;
    }
  
}


AuthorWithConflict.java

Java




public class AuthorWithConflict {
    
    public int id;
    public String name;
    boolean checked;
  
    public AuthorWithConflict() {
        super();
    }
  
    public AuthorWithConflict(final int id, final String name, final boolean checked) {
        this.id = id;
        this.name = name;
        this.checked = checked;
    }
  
    public boolean getChecked() {
        return checked;
    }
  
    public boolean isChecked() {
        return checked;
    }
}


AuthorWithNoDefaultConstructor.java

Java




public class AuthorWithNoDefaultConstructor {
  
    private int id;
    private String name;
  
    public AuthorWithNoDefaultConstructor(final int id, final String name) {
        this.id = id;
        this.name = name;
    }
  
    public int getId() {
        return id;
    }
  
    public String getName() {
        return name;
    }
  
}


AuthorWithPrivateFields.java

Java




public class AuthorWithPrivateFields {
    
    int id;
    String name;
  
    public AuthorWithPrivateFields() {
        super();
    }
  
    public AuthorWithPrivateFields(final int id, final String name) {
        this.id = id;
        this.name = name;
    }
  
}


AuthorWithRoot.java

Java




import com.fasterxml.jackson.annotation.JsonRootName;
  
@JsonRootName(value = "user")
public class AuthorWithRoot {
    public int id;
    public String name;
  
    public AuthorWithRoot() {
        super();
    }
  
    public AuthorWithRoot(final int id, final String name) {
        this.id = id;
        this.name = name;
    }
}


@JsonRootName annotation is used to indicate the name of the POJO, here AuthorWithRoot and it should be serialized. For serialization to work with @JsonRootName, enabling of SerializationFeature.WRAP_ROOT_VALUE  is needed. For deserialization,  enabling of DeserializationFeature.UNWRAP_ROOT_VALUE, is needed

ExampleForNoAccessors.java

Java




public class ExampleForNoAccessors {
    
    String stringValue;
    int intValue;
    boolean booleanValue;
  
    public ExampleForNoAccessors() {
        super();
    }
  
    public ExampleForNoAccessors(final String stringValue, final int intValue, final boolean booleanValue) {
        super();
  
        this.stringValue = stringValue;
        this.intValue = intValue;
        this.booleanValue = booleanValue;
    }
}


ExampleForNoAccessorsAndFieldVisibility.java

Java




import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
  
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class ExampleForNoAccessorsAndFieldVisibility {
  
    String stringValue;
    int intValue;
    boolean booleanValue;
  
    public ExampleForNoAccessorsAndFieldVisibility() {
        super();
    }
  
    public ExampleForNoAccessorsAndFieldVisibility(final String stringValue, final int intValue, final boolean booleanValue) {
        super();
  
        this.stringValue = stringValue;
        this.intValue = intValue;
        this.booleanValue = booleanValue;
    }
  
}


By default, Jackson can able to access public fields while doing serialization and deserialization. public getters/setters are the additional ways in case public fields are not present. By using @JsonAutoDetect annotation and its visibility role of setting to ANY, even the private fields can be accessible. If this is not given, we will end up with an exception saying as the model class does not have getter/setter methods. Let us add the test classes now to test it.

JacksonExceptionsUnitTestExample.java

Java




import static org.junit.matchers.JUnitMatchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
  
import java.io.IOException;
import java.util.List;
  
import org.junit.Test;
  
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import com.gfg.exceptions.Author;
import com.gfg.exceptions.AuthorWithNoDefaultConstructor;
import com.gfg.exceptions.AuthorWithPrivateFields;
import com.gfg.exceptions.AuthorWithRoot;
import com.gfg.exceptions.AnimalLives;
import com.gfg.exceptions.AnimalLivesConfigured;
  
public class JacksonExceptionsUnitTestExample {
  
    // JsonMappingException: Can not construct instance of
    @Test(expected = JsonMappingException.class)
    public void givenAbstractClass_whenDeserializing_thenException() throws IOException {
        final String json = "{\"animal\":{\"name\":\"rocky\"}}";
        final ObjectMapper mapper = new ObjectMapper();
  
        mapper.reader()
            .forType(AnimalLives.class)
            .readValue(json);
    }
  
    @Test
    public void givenAbstractClassConfigured_whenDeserializing_thenCorrect() throws IOException {
        final String json = "{\"animal\":{\"name\":\"bruce\"}}";
        final ObjectMapper mapper = new ObjectMapper();
  
        mapper.reader()
            .forType(AnimalLivesConfigured.class)
            .readValue(json);
    }
  
    // JsonMappingException: No serializer found for class
    @Test(expected = JsonMappingException.class)
    public void givenClassWithPrivateFields_whenSerializing_thenException() throws IOException {
        final AuthorWithPrivateFields user = new AuthorWithPrivateFields(1, "Rachel");
  
        final ObjectMapper mapper = new ObjectMapper();
        mapper.writer()
            .writeValueAsString(user);
    }
  
    @Test
    public void givenClassWithPrivateFields_whenConfigureSerializing_thenCorrect() throws IOException {
        final AuthorWithPrivateFields user = new AuthorWithPrivateFields(1, "Ross");
  
        final ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
  
        final String result = mapper.writer()
            .writeValueAsString(user);
        assertThat(result, containsString("Ross"));
    }
  
    // JsonMappingException: No suitable constructor found
    @Test(expected = JsonMappingException.class)
    public void givenNoDefaultConstructor_whenDeserializing_thenException() throws IOException {
        final String json = "{\"id\":1,\"name\":\"Chandler\"}";
        final ObjectMapper mapper = new ObjectMapper();
  
        mapper.reader()
            .forType(AuthorWithNoDefaultConstructor.class)
            .readValue(json);
    }
  
    @Test
    public void givenDefaultConstructor_whenDeserializing_thenCorrect() throws IOException {
        final String json = "{\"id\":1,\"name\":\"Joey\"}";
        final ObjectMapper mapper = new ObjectMapper();
  
        final Author user = mapper.reader()
            .forType(Author.class)
            .readValue(json);
        assertEquals("Joey", user.name);
    }
  
    // JsonMappingException: Root name does not match expected
    @Test(expected = JsonMappingException.class)
    public void givenWrappedJsonString_whenDeserializing_thenException() throws IOException {
        final String json = "{\"user\":{\"id\":1,\"name\":\"Monica\"}}";
  
        final ObjectMapper mapper = new ObjectMapper();
        mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
  
        mapper.reader()
            .forType(Author.class)
            .readValue(json);
    }
  
    @Test
    public void givenWrappedJsonStringAndConfigureClass_whenDeserializing_thenCorrect() throws IOException {
        final String json = "{\"user\":{\"id\":1,\"name\":\"Phoebe\"}}";
  
        final ObjectMapper mapper = new ObjectMapper();
        mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
  
        final AuthorWithRoot user = mapper.reader()
            .forType(AuthorWithRoot.class)
            .readValue(json);
        assertEquals("Phoebe", user.name);
    }
}


While checking with the methods available in the test classes, by using deserialization, exceptions are handled. Hence on running the above tests, we can see everything got passed

 

Let’s do one more test.

JacksonMappingExceptionUnitTestExample.java

Here field visibility testing is carried out. In ‘ExampleForNoAccessors’, we have private fields and hence they cannot be accessed without getter and setter methods. But in ‘ExampleForNoAccessorsAndFieldVisibility’, though we have private fields, they can be accessed easily without getter and setter but we have ‘@JsonAutoDetect’ annotation and its fieldVisibility is set to Visibility.ANY

Java




import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.containsString;
import java.io.IOException;
  
import org.junit.Test;
  
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.ObjectMapper;
  
public class JacksonMappingExceptionUnitTestExample { 
    
    @Test
    public final void givenObjectHasNoAccessors_whenSerializingWithPrivateFieldsVisibility_thenNoException() throws JsonParseException, IOException {
        final ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
        final String dtoAsString = objectMapper.writeValueAsString(new ExampleForNoAccessors());
        assertThat(dtoAsString, containsString("intValue"));
        assertThat(dtoAsString, containsString("stringValue"));
        assertThat(dtoAsString, containsString("booleanValue"));
    }
  
    @Test
    public final void givenObjectHasNoAccessorsButHasVisibleFields_whenSerializing_thenNoException() throws JsonParseException, IOException {
        final ObjectMapper objectMapper = new ObjectMapper();
        final String dtoAsString = objectMapper.writeValueAsString(new ExampleForNoAccessorsAndFieldVisibility());
  
        assertThat(dtoAsString, containsString("intValue"));
        assertThat(dtoAsString, containsString("stringValue"));
        assertThat(dtoAsString, containsString("booleanValue"));
    }
  
}


On running the above JUNIT code, we get the following results

 

Conclusion

Throughout a software project, we will come across multiple JSON outputs. While handling JSON, if they are not deserialized properly, Jackson exceptions will arise. We have to commonly use certain annotations like

  • @JsonDeserialize(as = <Required class name>)
  • @JsonRootName(value = “<class name>”)
  • @JsonAutoDetect(fieldVisibility = Visibility.ANY)

By using the annotations in the required places, we can come over the Jackson exceptions.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads