Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save eeichinger/3d0cfa5b8b3f09e7d1c20f5ce4a3fe12 to your computer and use it in GitHub Desktop.
Save eeichinger/3d0cfa5b8b3f09e7d1c20f5ce4a3fe12 to your computer and use it in GitHub Desktop.
Example tests and notes for making Jackson work with Lombok

Examples for getting Jackson and Lombok to work together to create immutable data types.

Demonstrates use of:

  • Nullable Types
  • Optional
  • immutable java.util.List
  • immutable array
  • validating custom types on instantiate/unmarshalling
  • use & customize Lombok-@Builder with Jackson

Run

clone this gist and run with maven

git clone git@gist.github.com:3d0cfa5b8b3f09e7d1c20f5ce4a3fe12.git jackson-lombok-test && cd jackson-lombok-test && mvn test  

Requires:

  • Jackson >= 2.7.0
  • Lombok >= 1.16.6

TL;DR - just example datatypes with comments

@Value
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder(toBuilder = true)
@JsonDeserialize(builder = ImmutableDataType.ImmutableDataTypeBuilder.class)
final class ImmutableDataType {

    @NonNull // generate NULL check when setting value
        String mandatoryValue;

    @JsonInclude(Include.NON_NULL) // don't write value if null
        String nullableValue;

    @JsonInclude(Include.NON_EMPTY) // don't write value if empty
    @NonNull // generate NULL check when setting value
        Optional<String> optionalValue;

    // declare field using ImmutableList prevents a) setting a mutable list and b) getting a mutable list
    @JsonInclude(Include.NON_EMPTY) // don't write null or empty list
    @NonNull // generate NULL check when setting value
        ImmutableList<ImmutableStringElement> listValue;

    @JsonInclude(Include.NON_EMPTY) // don't write null or empty array
    @NonNull // generate NULL check when setting value
        ImmutableEnumListElement[] arrayValue;

    /**
     * we can override get logic if needed (e.g. for arrays) - wish Lombok would do this for @Value types
     * <p>
     * OTOH, this is one of the reasons why you should never use plain arrays.
     *
     * @return list of elements, never {@code null}
     */
    public ImmutableEnumListElement[] getArrayValue() {
        return arrayValue.clone();
    }

    /**
     * define builder class - Lombok will enhance this class as needed, Jackson will use this builder then through the @JsonDeserialize annotation above
     */
    @JsonPOJOBuilder(withPrefix = "")
    @JsonIgnoreProperties(ignoreUnknown = true) // don't barf on unknown properties during deserialization
    public static class ImmutableDataTypeBuilder {
        /**
         * we may set default values for non-nullables here
         */
        protected ImmutableDataTypeBuilder() {
            this.optionalValue = Optional.empty();
            this.listValue = ImmutableList.of();
            this.arrayValue = new ImmutableEnumListElement[0];
        }

        /**
         * we can override set logic if needed here (e.g. for arrays)
         *
         * @return list of elements, never {@code null}
         */
        public ImmutableDataTypeBuilder arrayValue(@NonNull ImmutableEnumListElement[] arrayValue) {
            this.arrayValue = arrayValue.clone();
            return this;
        }
    }

}

@Value
@AllArgsConstructor(access = AccessLevel.PRIVATE) // mark instance ctor private, only static ctor may use it
final class ImmutableEnumListElement {
    public enum Colour {
        RED, BLUE, GREEN, YELLOW
    }

    @NonNull
    Colour[] colours;

    /**
     * Again, I wished Lombok did clone immutable arrays automatically
     */
    public Colour[] getColours() {
        return colours.clone();
    }

    /**
     * use this method to generate the JSON representation
     */
    @JsonValue
    public String toString() {
        return String.join(",", Stream.of(colours).map(c -> c.toString()).toArray(String[]::new));
    }

    /**
     * use this method to deserialize from a JSON representation. Any instantiation (code, Jackson & Spring MVC) will go through this
     * static ctor - hence put any validation & parsing logic here
     */
    @JsonCreator
    public static ImmutableEnumListElement of(@NonNull String commaSeparatedColourList) {
        if (commaSeparatedColourList.trim().length() == 0) throw new IllegalArgumentException("colour list must not be empty");
        return new ImmutableEnumListElement(Stream
            .of(commaSeparatedColourList.split(","))
            .map(strColour -> strColour.trim())
            .map(strColour -> Colour.valueOf(strColour.toUpperCase()))
            .toArray(Colour[]::new)
        );
    }
}

@Value
@AllArgsConstructor(access = AccessLevel.PRIVATE) // mark instance ctor private, only static ctor may use it
final class ImmutableStringElement {
    @NonNull
    String elementName;

    /**
     * any instantiation (code, Jackson & Spring MVC) will go through this static ctor - put validation logic here
     */
    @JsonCreator
    public static ImmutableStringElement of(@JsonProperty("elementName") @NonNull String elementName) {
        // put validation logic here
        if (elementName.length() < 5) throw new IllegalArgumentException("name length must be >5");
        return new ImmutableStringElement(elementName);
    }
}
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalField;
import java.util.Date;
import java.util.Optional;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.guava.GuavaModule;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaDateFormat;
import com.fasterxml.jackson.datatype.joda.ser.DateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.collect.ImmutableList;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
/**
* <p>Examples for getting Jackson and Lombok to work together to create immutable data types.
* <p>Demonstrates use of:
* <ul>
* <li>Nullable Types
* <li>{@link Optional}
* <li>immutable {@link java.util.List}
* <li>immutable array
* <li>validating custom types on instantiate/unmarshalling
* <li>use & customize Lombok-@Builder with Jackson
* </ul>
* <p>Requires:
* <ul>
* <li>Jackson >= 2.7.0
* <li>Lombok >= 1.16.6
* </ul>
*
* @author Erich Eichinger
* @since 29/08/16
*/
public class Immutable_Jackson_Lombok_Test {
@Rule
public ExpectedException thrown = ExpectedException.none();
ObjectMapper om = new ObjectMapper()
.registerModule(new Jdk8Module())
.registerModule(new GuavaModule())
.registerModule(new JavaTimeModule())
.registerModule(new JodaModule())
.setDateFormat(new com.fasterxml.jackson.databind.util.ISO8601DateFormat())
.findAndRegisterModules() // for auto-discovery from classpath instead of manual registration
;
@Test
public void serialize_writes_all_values() throws Exception {
ImmutableDataType data = ImmutableDataType
.builder()
.mandatoryValue("some mandatory value")
.nullableValue("some nullable value")
.optionalValue(Optional.of("some optional value"))
.optionalDateValue(Optional.of(Date.from(ZonedDateTime.of(2016, 1, 2, 13, 14, 15, 678000000, ZoneOffset.UTC).toInstant())))
.optionalZonedDateTimeValue(Optional.of(ZonedDateTime.of(2016, 1, 2, 13, 14, 15, 678000000, ZoneOffset.UTC)))
.optionalJodaDateValue(Optional.of(new DateTime(2016, 2, 3, 14, 15, 16, 789, DateTimeZone.UTC)))
.listValue(ImmutableList.of(
ImmutableStringElement.of("entry 1")
, ImmutableStringElement.of("entry 2")
))
.arrayValue(new ImmutableEnumListElement[]{
ImmutableEnumListElement.of("blue , red, green ")
, ImmutableEnumListElement.of(" yellow, \ngreen ")
})
.build();
String json = om.writeValueAsString(data);
assertThat(json, equalTo("{" +
"\"mandatoryValue\":\"some mandatory value\"" +
",\"nullableValue\":\"some nullable value\"" +
",\"optionalValue\":\"some optional value\"" +
",\"optionalDateValue\":1451740455678" +
",\"optionalZonedDateTimeValue\":\"2016-01-02T13:14:15.678Z\"" +
",\"optionalJodaDateValue\":\"2016-02-03T14:15:16.789+0000\"" +
",\"listValue\":[{\"elementName\":\"entry 1\"},{\"elementName\":\"entry 2\"}]" +
",\"arrayValue\":[\"BLUE,RED,GREEN\",\"YELLOW,GREEN\"]}"
)
);
}
@Test
public void serialize_omits_null_and_empty() throws Exception {
ImmutableDataType data = ImmutableDataType
.builder()
.mandatoryValue("some mandatory value")
.build();
String json = om.writeValueAsString(data);
assertThat(json, equalTo("{\"mandatoryValue\":\"some mandatory value\"}"));
}
@Test
public void deserialize_reads_all_values() throws Exception {
ImmutableDataType data = om.readValue("{" +
"\"mandatoryValue\":\"some mandatory value\"" +
",\"nullableValue\":\"some nullable value\"" +
",\"optionalValue\":\"some optional value\"" +
",\"optionalDateValue\":1451740455678" +
",\"optionalZonedDateTimeValue\":\"2016-01-02T13:14:15.678Z\"" +
",\"optionalJodaDateValue\":\"2016-02-03T14:15:16.789+0000\"" +
",\"listValue\":[{\"elementName\":\"entry 1\"},{\"elementName\":\"entry 2\"}]" +
",\"arrayValue\":[\" blue , \\nred,green\",\"YeLLoW,GREEN\"]" +
"}"
, ImmutableDataType.class
);
assertThat(data.getMandatoryValue(), equalTo("some mandatory value"));
assertThat(data.getNullableValue(), equalTo("some nullable value"));
assertThat(data.getOptionalValue().get(), equalTo("some optional value"));
assertThat(data.getOptionalDateValue().get(), equalTo(Date.from(ZonedDateTime.of(2016, 1, 2, 13, 14, 15, 678000000, ZoneOffset.UTC).toInstant())));
assertThat(data.getOptionalZonedDateTimeValue().get().withFixedOffsetZone(), equalTo(ZonedDateTime.of(2016, 1, 2, 13, 14, 15, 678000000, ZoneOffset.UTC)));
assertThat(data.getOptionalJodaDateValue().get(), equalTo(new DateTime(2016, 2, 3, 14, 15, 16, 789, DateTimeZone.UTC)));
assertThat(data.getListValue().size(), equalTo(2));
assertThat(data.getListValue().get(0), equalTo(ImmutableStringElement.of("entry 1")));
assertThat(data.getListValue().get(1), equalTo(ImmutableStringElement.of("entry 2")));
assertThat(data.getArrayValue().length, equalTo(2));
assertThat(data.getArrayValue()[0].getColours().length, equalTo(3));
assertThat(data.getArrayValue()[1].getColours().length, equalTo(2));
}
@Test
public void deserialize_use_defaults_for_optionals() throws Exception {
ImmutableDataType data = om.readValue(
"{\"mandatoryValue\":\"some mandatory value\"}"
, ImmutableDataType.class
);
assertThat(data.getMandatoryValue(), equalTo("some mandatory value"));
assertThat(data.getOptionalValue(), equalTo(Optional.empty()));
assertThat(data.getNullableValue(), nullValue());
assertThat(data.getListValue().size(), equalTo(0));
assertThat(data.getArrayValue().length, equalTo(0));
}
@Test
public void instantiate_with_defaults_from_builder() {
ImmutableDataType data = ImmutableDataType
.builder()
.mandatoryValue("some mandatory value")
.build();
assertThat(data.getMandatoryValue(), equalTo("some mandatory value"));
assertThat(data.getNullableValue(), nullValue());
assertThat(data.getOptionalValue().isPresent(), equalTo(false));
assertThat(data.getListValue().size(), equalTo(0));
assertThat(data.getArrayValue().length, equalTo(0));
}
@Test
public void instantiate_throws_on_missing_mandatory() {
thrown.expectMessage("mandatoryValue");
thrown.expect(NullPointerException.class);
ImmutableDataType data = ImmutableDataType
.builder()
.build();
}
@Test
public void instantiate_throws_on_null_mandatory() {
thrown.expectMessage("mandatoryValue");
thrown.expect(NullPointerException.class);
ImmutableDataType data = ImmutableDataType
.builder()
.mandatoryValue(null)
.build();
}
@Test
public void listValue_is_immutable() {
ImmutableDataType data = ImmutableDataType
.builder()
.mandatoryValue("some mandatory value")
.listValue(ImmutableList.of(ImmutableStringElement.of("entry 1")))
.build();
thrown.expect(UnsupportedOperationException.class);
// list can't be modified
data.getListValue().removeIf(el -> true);
}
@Test
public void arrayValue_is_immutable() {
ImmutableDataType data = ImmutableDataType
.builder()
.mandatoryValue("some mandatory value")
.arrayValue(new ImmutableEnumListElement[]{
ImmutableEnumListElement.of("red, blue, green")
})
.build();
// we only get a copy, hence original array can't be modified
data.getArrayValue()[0] = null;
//noinspection ConstantConditions
assertThat(data.getArrayValue()[0].toString(), equalTo("RED,BLUE,GREEN"));
}
@Test
public void arrayValue_gets_cloned_in_ctor() {
final ImmutableEnumListElement[] arrayValue = {
ImmutableEnumListElement.of("red, blue, green")
};
ImmutableDataType data = ImmutableDataType
.builder()
.mandatoryValue("some mandatory value")
.arrayValue(arrayValue)
.build();
// modifying original array as no effect
arrayValue[0] = null;
//noinspection ConstantConditions
assertThat(data.getArrayValue()[0].toString(), equalTo("RED,BLUE,GREEN"));
}
@Test
public void deserialize_ignores_unknown() throws Exception {
ImmutableDataType data = om.readValue("{" +
"\"mandatoryValue\":\"some mandatory value\"" +
", \"unknownValue\":\"some unknown value\"" +
"}"
, ImmutableDataType.class
);
assertThat(data.getMandatoryValue(), equalTo("some mandatory value"));
assertThat(data.getListValue().size(), equalTo(0));
}
@Test
public void deserialize_throws_on_null_mandatoryValue() throws Exception {
// null value
try {
om.readValue("{\"mandatoryValue\":null}", ImmutableDataType.class
);
throw new AssertionError("should never get here");
} catch (JsonMappingException jme) {
assertThat(jme.getMessage(), containsString("problem: mandatoryValue"));
assertThat(jme.getCause(), instanceOf(NullPointerException.class));
assertThat(jme.getCause().getMessage(), equalTo("mandatoryValue"));
}
}
@Test
public void deserialize_throws_on_missing_mandatoryValue() throws Exception {
// missing value
try {
om.readValue("{}", ImmutableDataType.class);
throw new AssertionError("should never get here");
} catch (JsonMappingException jme) {
assertThat(jme.getCause(), instanceOf(NullPointerException.class));
assertThat(jme.getCause().getMessage(), equalTo("mandatoryValue"));
}
}
}
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.Optional;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Feature;
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.google.common.collect.ImmutableList;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import org.joda.time.DateTime;
/**
* This class demonstrates the canonical immutable type for use with Jackson's serialization/deserialization.
* All properties are immutable, including collections and arrays
*/
@Value
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder(toBuilder = true)
@JsonDeserialize(builder = ImmutableDataType.ImmutableDataTypeBuilder.class)
public final class ImmutableDataType {
@NonNull // generate NULL check when setting value
String mandatoryValue;
@JsonInclude(Include.NON_NULL) // don't write value if null
String nullableValue;
@JsonInclude(Include.NON_EMPTY) // don't write value if empty
@NonNull // generate NULL check when setting value
Optional<String> optionalValue;
// if you don't like the default date format, use @JsonFormat
@JsonFormat(shape = Shape.NUMBER_INT) // format java.util.Date as timestamp epoche millis
// @JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ") // format using SimpleDateFormat pattern
@JsonInclude(Include.NON_EMPTY) // don't write value if empty
@NonNull // generate NULL check when setting value
Optional<Date> optionalDateValue;
// if you don't like the default date format, use @JsonFormat
// @JsonFormat(shape = Shape.NUMBER_FLOAT, with={Feature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS}) // format as timestamp
// @JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ") // format using SimpleDateFormat pattern
@JsonInclude(Include.NON_EMPTY) // don't write value if empty
@NonNull // generate NULL check when setting value
Optional<ZonedDateTime> optionalZonedDateTimeValue;
@JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
@JsonInclude(Include.NON_EMPTY) // don't write value if empty
@NonNull // generate NULL check when setting value
Optional<DateTime> optionalJodaDateValue;
// declare using ImmutableList prevents a) setting a mutable list and b) getting a mutable list
@JsonInclude(Include.NON_EMPTY) // don't write null or empty list
@NonNull // generate NULL check when setting value
ImmutableList<ImmutableStringElement> listValue;
@JsonInclude(Include.NON_EMPTY) // don't write null or empty list
@NonNull // generate NULL check when setting value
ImmutableEnumListElement[] arrayValue;
/**
* we can override get logic if needed (e.g. for arrays) - wish Lombok would do this for @Value types
* <p>
* OTOH, this is one of the reasons why you should never use plain arrays.
*
* @return list of elements, never {@code null}
*/
public ImmutableEnumListElement[] getArrayValue() {
return arrayValue.clone();
}
/**
* define builder class - Lombok will enhance this class as needed, Jackson will use this builder then through the @JsonDeserialize annotation above
*/
@JsonPOJOBuilder(withPrefix = "")
@JsonIgnoreProperties(ignoreUnknown = true) // don't barf on unknown properties during deserialization
public static class ImmutableDataTypeBuilder {
/**
* we may set default values for non-nullables here
*/
protected ImmutableDataTypeBuilder() {
this.optionalValue = Optional.empty();
this.optionalDateValue = Optional.empty();
this.optionalZonedDateTimeValue = Optional.empty();
this.optionalJodaDateValue = Optional.empty();
this.listValue = ImmutableList.of();
this.arrayValue = new ImmutableEnumListElement[0];
}
/**
* we can override set logic if needed here (e.g. for arrays)
*
* @return list of elements, never {@code null}
*/
public ImmutableDataTypeBuilder arrayValue(@NonNull ImmutableEnumListElement[] arrayValue) {
this.arrayValue = arrayValue.clone();
return this;
}
// no need to make list immutable during construction when using Guava's ImmutableList as above
// public ImmutableDataTypeBuilder listValue(List<ImmutableStringElement> listValue) {
// this.listValue = Collections.unmodifiableList(listValue == null ? emptyList() : listValue);
// return this;
// }
}
}
import java.util.stream.Stream;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.Value;
/**
* This type demonstrates an immutable single-value type-wrapper around an enum-list for use with Code, Jackson and Spring-MVC
*/
@Value
@AllArgsConstructor(access = AccessLevel.PRIVATE) // mark instance ctor private, only static ctor may use it
public final class ImmutableEnumListElement {
public enum Colour {
RED, BLUE, GREEN, YELLOW
}
@NonNull
Colour[] colours;
/**
* Again, I wished Lombok did clone immutable arrays automatically
*/
public Colour[] getColours() {
return colours.clone();
}
/**
* use this method to generate the JSON representation
*/
@JsonValue
public String toString() {
return String.join(",", Stream.of(colours).map(c -> c.toString()).toArray(String[]::new));
}
/**
* use this method to deserialize from a JSON representation. Any instantiation (code, Jackson & Spring MVC) will go through this
* static ctor - hence put any validation & parsing logic here
*/
@JsonCreator
public static ImmutableEnumListElement of(@NonNull String commaSeparatedColourList) {
if (commaSeparatedColourList.trim().length() == 0) throw new IllegalArgumentException("colour list must not be empty");
return new ImmutableEnumListElement(Stream
.of(commaSeparatedColourList.split(","))
.map(strColour -> strColour.trim())
.map(strColour -> Colour.valueOf(strColour.toUpperCase()))
.toArray(Colour[]::new)
);
}
}
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.Value;
/**
* For "single-value" data types, Jackson and Spring MVC automatically look for instance or static "string" constructors.
* <p>
* We use @JsonCreator and a custom static ctor to add validation or any parsing logic to String
*/
@Value
@AllArgsConstructor(access = AccessLevel.PRIVATE) // mark instance ctor private, only static ctor may use it
public final class ImmutableStringElement {
@NonNull
String elementName;
/**
* any instantiation (code, Jackson & Spring MVC) will go through this static ctor - put validation logic here
*/
@JsonCreator
public static ImmutableStringElement of(@JsonProperty("elementName") @NonNull String elementName) {
// put validation logic here
if (elementName.length() < 5) throw new IllegalArgumentException("name length must be >5");
return new ImmutableStringElement(elementName);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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>
<groupId>com.github.eeichinger.gists</groupId>
<artifactId>jackson-lombok-tests</artifactId>
<version>0.0.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<testSourceDirectory>${project.basedir}</testSourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-guava</artifactId>
<version>2.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>2.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
<version>2.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment