diff --git a/build.gradle b/build.gradle index 7978949..bebce83 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,9 @@ repositories { mavenCentral() } +compileScala.targetCompatibility = 1.8 +ScalaCompileOptions.metaClass.useAnt = false + dependencies { compile group: 'org.scala-lang', name: 'scala-library', version: '2.12.8' compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.1.3.RELEASE' diff --git a/src/main/scala/org/example/configuration/JacksonConfiguration.scala b/src/main/scala/org/example/configuration/JacksonConfiguration.scala index c0ba5b3..bba042d 100644 --- a/src/main/scala/org/example/configuration/JacksonConfiguration.scala +++ b/src/main/scala/org/example/configuration/JacksonConfiguration.scala @@ -5,9 +5,12 @@ import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.fasterxml.jackson.databind.{DeserializationContext, JsonNode, ObjectMapper, SerializerProvider} import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.databind.ser.std.StdSerializer -import com.fasterxml.jackson.module.scala.DefaultScalaModule -import org.example.configuration.JacksonConfiguration.{GenderJsonDeserializer, GenderJsonSerializer} -import org.example.model.Person.Gender +import com.fasterxml.jackson.module.scala.deser.{ScalaNumberDeserializersModule, UntypedObjectDeserializerModule} +import com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule +import com.fasterxml.jackson.module.scala.{DefaultScalaModule, EitherModule, EnumerationModule, IterableModule, IteratorModule, JacksonModule, MapModule, OptionModule, SeqModule, SetModule, TupleModule} +import org.example.configuration.JacksonConfiguration.{FaceJsonDeserializer, FaceJsonSerializer, GenderJsonDeserializer, GenderJsonSerializer} +import org.example.model.Person.Face.Face +import org.example.model.Person.{Face, FaceEnum, Gender, GenderEnum} import org.example.model.Person.Gender.Gender import org.springframework.context.annotation.{Bean, Configuration} @@ -17,11 +20,17 @@ class JacksonConfiguration { @Bean def objectMapper: ObjectMapper = { val objectMapper = new ObjectMapper() - objectMapper.registerModule(DefaultScalaModule) - val genderSerializerModule = new SimpleModule() - genderSerializerModule.addSerializer(new GenderJsonSerializer()) - genderSerializerModule.addDeserializer(classOf[Gender.Gender], new GenderJsonDeserializer()) - objectMapper.registerModule(genderSerializerModule) + objectMapper.registerModule(MyScalaModule) + val genderSerializerModule = new SimpleModule() + genderSerializerModule.addSerializer(new GenderJsonSerializer()) + genderSerializerModule.addDeserializer(classOf[Gender.Gender], new GenderJsonDeserializer()) + objectMapper.registerModule(genderSerializerModule) + + val faceSerializerModule = new SimpleModule() + faceSerializerModule.addSerializer(new FaceJsonSerializer()) + faceSerializerModule.addDeserializer(classOf[Face], new FaceJsonDeserializer()) + objectMapper.registerModule(faceSerializerModule) + objectMapper } } @@ -30,7 +39,7 @@ object JacksonConfiguration { class GenderJsonSerializer extends StdSerializer[Gender.Gender](classOf[Gender.Gender]) { - def this(t: Gender.type) = this() + def this(t: GenderEnum) = this() override def serialize(value: Gender, gen: JsonGenerator, provider: SerializerProvider): Unit = gen.writeString(value.toString) @@ -38,10 +47,45 @@ object JacksonConfiguration { class GenderJsonDeserializer extends StdDeserializer[Gender.Gender](classOf[Gender.Gender]) { - def this(t: Gender.type ) = this() + def this(t: GenderEnum) = this() override def deserialize(p: JsonParser, ctxt: DeserializationContext): Gender = Gender.withName(p.getCodec.readTree(p).asInstanceOf[JsonNode].asText()) } + + class FaceJsonSerializer extends StdSerializer[Face](classOf[Face]) { + + def this(t: FaceEnum) = this() + + override def serialize(value: Face, gen: JsonGenerator, provider: SerializerProvider): Unit = + gen.writeString(value.toString) + } + + class FaceJsonDeserializer extends StdDeserializer[Face](classOf[Face]) { + + def this(t: FaceEnum) = this() + + override def deserialize(p: JsonParser, ctxt: DeserializationContext): Face = + Face.withName(p.getCodec.readTree(p).asInstanceOf[JsonNode].asText()) + } + } + +class MyScalaModule + extends JacksonModule + with IteratorModule + with OptionModule + with SeqModule + with IterableModule + with TupleModule + with MapModule + with SetModule + with ScalaNumberDeserializersModule + with ScalaAnnotationIntrospectorModule + with UntypedObjectDeserializerModule + with EitherModule { + override def getModuleName = "MyScalaModule" +} + +object MyScalaModule extends MyScalaModule diff --git a/src/main/scala/org/example/model/Person.scala b/src/main/scala/org/example/model/Person.scala index 96e2655..b1458b0 100644 --- a/src/main/scala/org/example/model/Person.scala +++ b/src/main/scala/org/example/model/Person.scala @@ -1,6 +1,9 @@ package org.example.model -import org.example.model.Person.Gender +import java.util + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import org.example.model.Person.{Face, Gender} import scala.beans.BeanProperty import scala.util.Random @@ -9,19 +12,39 @@ case class Person( @BeanProperty var name: String, @BeanProperty var age: Int, @BeanProperty var hobbies: List[String], + + @BeanProperty + @JsonDeserialize(using = classOf[org.example.configuration.JacksonConfiguration.GenderJsonDeserializer]) + var gender: Gender.Gender, + @BeanProperty - var gender: Gender.Gender + @JsonDeserialize(using = classOf[org.example.configuration.JacksonConfiguration.FaceJsonDeserializer]) + var face: Face.Face, + ) { - def this() = this("", 0, List(), Gender.MALE) + def this() = this("", 0, List(), Gender.MALE, Face.BIG_FACE) } object Person { - object Gender extends Enumeration { + trait GenderEnum { + this: scala.Enumeration => + } + + object Gender extends Enumeration with GenderEnum { type Gender = Value val MALE, FEMALE = Value } + trait FaceEnum { + this: scala.Enumeration => + } + + object Face extends Enumeration with FaceEnum { + type Face = Value + val BIG_FACE, SMALL_FACE = Value + } + private val names = List("Zorro", "Morro", "Korro") private val hobbies = List("killing", "stealing", "hiking", "swimming", "sleeping") @@ -29,6 +52,7 @@ object Person { Random.shuffle(names).head, Random.nextInt(100), Random.shuffle(hobbies).take(2), - Random.shuffle(Gender.values.toList).head + Random.shuffle(Gender.values.toList).head, + Random.shuffle(Face.values.toList).head ) } \ No newline at end of file diff --git a/src/test/scala/org/example/JsonSerializationTest.scala b/src/test/scala/org/example/JsonSerializationTest.scala index 5eb676b..27e75a8 100644 --- a/src/test/scala/org/example/JsonSerializationTest.scala +++ b/src/test/scala/org/example/JsonSerializationTest.scala @@ -2,7 +2,7 @@ package org.example import org.example.configuration.JacksonConfiguration import org.example.model.Person -import org.example.model.Person.Gender +import org.example.model.Person.{Face, Gender} import org.junit.runner.RunWith import org.scalatest.{FlatSpec, Matchers} import org.scalatestplus.junit.JUnitRunner @@ -11,31 +11,31 @@ import org.scalatestplus.junit.JUnitRunner class JsonSerializationTest extends FlatSpec with Matchers { "Jackson serializer" should "serialize a Person into JSON" in { - val samplePerson = Person("Zorro", 15, List("Lego", "Piano"), Gender.MALE) + val samplePerson = Person("Zorro", 15, List("Lego", "Piano"), Gender.MALE, Face.SMALL_FACE) val mapper = new JacksonConfiguration().objectMapper val actualJson = mapper.writerFor(classOf[Person]).writeValueAsString(samplePerson) - val expectedJson = """{"name":"Zorro","age":15,"hobbies":["Lego","Piano"],"gender":"MALE"}""" + val expectedJson = """{"name":"Zorro","age":15,"hobbies":["Lego","Piano"],"gender":"MALE","face":"SMALL_FACE"}""" actualJson should equal(expectedJson) } it should "deserialize a Person from JSON string" in { - val sampleJson = """{"name":"Zorro","age":15,"hobbies":["Lego","Piano"],"gender":"MALE"}""" + val sampleJson = """{"name":"Zorro","age":15,"hobbies":["Lego","Piano"],"gender":"MALE","face":"BIG_FACE"}""" val mapper = new JacksonConfiguration().objectMapper val actualPerson = mapper.readValue(sampleJson, classOf[Person]) - val expectedPerson = Person("Zorro", 15, List("Lego", "Piano"), Gender.MALE) + val expectedPerson = Person("Zorro", 15, List("Lego", "Piano"), Gender.MALE, Face.BIG_FACE) actualPerson should equal(expectedPerson) } it should "serialize and deserialize random Persons 1000 times" in { - for(i <- 0 to 1000) { + for (i <- 0 to 1000) { val randomPerson = Person.randomPerson val mapper = new JacksonConfiguration().objectMapper val jsonString = mapper.writerFor(classOf[Person]).writeValueAsString(randomPerson) val actualPerson = mapper.readValue(jsonString, classOf[Person]) - actualPerson should equal (randomPerson) + actualPerson should equal(randomPerson) } } }