From fb1c1042e935fe82edf9f74ce9420a1bc567bfb9 Mon Sep 17 00:00:00 2001 From: ssh Date: Fri, 17 Apr 2020 13:00:32 +0200 Subject: [PATCH 1/4] two enums com.fasterxml.jackson.databind.JsonMappingException: No value found for 'MALE' (through reference chain: org.example.model.Person["gender"]) --- build.gradle | 3 ++ .../configuration/JacksonConfiguration.scala | 30 +++++++++++++++++-- src/main/scala/org/example/model/Person.scala | 18 ++++++++--- .../org/example/JsonSerializationTest.scala | 14 ++++----- 4 files changed, 51 insertions(+), 14 deletions(-) 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..9f3edb2 100644 --- a/src/main/scala/org/example/configuration/JacksonConfiguration.scala +++ b/src/main/scala/org/example/configuration/JacksonConfiguration.scala @@ -6,8 +6,9 @@ import com.fasterxml.jackson.databind.{DeserializationContext, JsonNode, ObjectM 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 org.example.configuration.JacksonConfiguration.{FaceJsonDeserializer, FaceJsonSerializer, GenderJsonDeserializer, GenderJsonSerializer} +import org.example.model.Person.Face.Face +import org.example.model.Person.{Face, Gender} import org.example.model.Person.Gender.Gender import org.springframework.context.annotation.{Bean, Configuration} @@ -22,6 +23,12 @@ class JacksonConfiguration { 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.Face], new FaceJsonDeserializer()) + objectMapper.registerModule(faceSerializerModule) + objectMapper } } @@ -38,10 +45,27 @@ object JacksonConfiguration { class GenderJsonDeserializer extends StdDeserializer[Gender.Gender](classOf[Gender.Gender]) { - def this(t: Gender.type ) = this() + def this(t: Gender.type) = this() override def deserialize(p: JsonParser, ctxt: DeserializationContext): Gender = Gender.withName(p.getCodec.readTree(p).asInstanceOf[JsonNode].asText()) } + + class FaceJsonSerializer extends StdSerializer[Face.Face](classOf[Face.Face]) { + + def this(t: Face.type) = this() + + override def serialize(value: Face, gen: JsonGenerator, provider: SerializerProvider): Unit = + gen.writeString(value.toString) + } + + class FaceJsonDeserializer extends StdDeserializer[Face.Face](classOf[Face.Face]) { + + def this(t: Face.type) = this() + + override def deserialize(p: JsonParser, ctxt: DeserializationContext): Face = + Face.withName(p.getCodec.readTree(p).asInstanceOf[JsonNode].asText()) + } + } diff --git a/src/main/scala/org/example/model/Person.scala b/src/main/scala/org/example/model/Person.scala index 96e2655..98dff12 100644 --- a/src/main/scala/org/example/model/Person.scala +++ b/src/main/scala/org/example/model/Person.scala @@ -1,6 +1,6 @@ package org.example.model -import org.example.model.Person.Gender +import org.example.model.Person.{Face, Gender} import scala.beans.BeanProperty import scala.util.Random @@ -10,9 +10,13 @@ case class Person( @BeanProperty var age: Int, @BeanProperty var hobbies: List[String], @BeanProperty - var gender: Gender.Gender + var gender: Gender.Gender, + + @BeanProperty + var face: Face.Face, + ) { - def this() = this("", 0, List(), Gender.MALE) + def this() = this("", 0, List(), Gender.MALE, Face.BIG_FACE) } object Person { @@ -22,6 +26,11 @@ object Person { val MALE, FEMALE = Value } + object Face extends Enumeration { + 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 +38,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) } } } From b1c27c0046ff1091ac3ba8f0fa9ea066f1fae40e Mon Sep 17 00:00:00 2001 From: ssh Date: Fri, 17 Apr 2020 13:25:25 +0200 Subject: [PATCH 2/4] It works --- .../configuration/JacksonConfiguration.scala | 12 ++++++------ src/main/scala/org/example/model/Person.scala | 17 +++++++++++++++-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/main/scala/org/example/configuration/JacksonConfiguration.scala b/src/main/scala/org/example/configuration/JacksonConfiguration.scala index 9f3edb2..93c2559 100644 --- a/src/main/scala/org/example/configuration/JacksonConfiguration.scala +++ b/src/main/scala/org/example/configuration/JacksonConfiguration.scala @@ -8,7 +8,7 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer import com.fasterxml.jackson.module.scala.DefaultScalaModule import org.example.configuration.JacksonConfiguration.{FaceJsonDeserializer, FaceJsonSerializer, GenderJsonDeserializer, GenderJsonSerializer} import org.example.model.Person.Face.Face -import org.example.model.Person.{Face, Gender} +import org.example.model.Person.{Face, FaceEnum, Gender} import org.example.model.Person.Gender.Gender import org.springframework.context.annotation.{Bean, Configuration} @@ -26,7 +26,7 @@ class JacksonConfiguration { val faceSerializerModule = new SimpleModule() faceSerializerModule.addSerializer(new FaceJsonSerializer()) - faceSerializerModule.addDeserializer(classOf[Face.Face], new FaceJsonDeserializer()) + faceSerializerModule.addDeserializer(classOf[Face], new FaceJsonDeserializer()) objectMapper.registerModule(faceSerializerModule) objectMapper @@ -52,17 +52,17 @@ object JacksonConfiguration { } - class FaceJsonSerializer extends StdSerializer[Face.Face](classOf[Face.Face]) { + class FaceJsonSerializer extends StdSerializer[Face](classOf[Face]) { - def this(t: Face.type) = this() + def this(t: Class[FaceEnum]) = this() override def serialize(value: Face, gen: JsonGenerator, provider: SerializerProvider): Unit = gen.writeString(value.toString) } - class FaceJsonDeserializer extends StdDeserializer[Face.Face](classOf[Face.Face]) { + class FaceJsonDeserializer extends StdDeserializer[Face](classOf[Face]) { - def this(t: Face.type) = this() + def this(t: Class[FaceEnum]) = this() override def deserialize(p: JsonParser, ctxt: DeserializationContext): Face = Face.withName(p.getCodec.readTree(p).asInstanceOf[JsonNode].asText()) diff --git a/src/main/scala/org/example/model/Person.scala b/src/main/scala/org/example/model/Person.scala index 98dff12..6ac9a8f 100644 --- a/src/main/scala/org/example/model/Person.scala +++ b/src/main/scala/org/example/model/Person.scala @@ -1,5 +1,8 @@ package org.example.model +import java.util + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize import org.example.model.Person.{Face, Gender} import scala.beans.BeanProperty @@ -10,9 +13,11 @@ case class Person( @BeanProperty var age: Int, @BeanProperty var hobbies: List[String], @BeanProperty + @JsonDeserialize(using = classOf[org.example.configuration.JacksonConfiguration.GenderJsonDeserializer]) var gender: Gender.Gender, @BeanProperty + @JsonDeserialize(using = classOf[org.example.configuration.JacksonConfiguration.FaceJsonDeserializer]) var face: Face.Face, ) { @@ -21,12 +26,20 @@ case class Person( object Person { - object Gender extends Enumeration { + trait GenderEnum { + this: scala.Enumeration => + } + + object Gender extends Enumeration with GenderEnum { type Gender = Value val MALE, FEMALE = Value } - object Face extends Enumeration { + trait FaceEnum { + this: scala.Enumeration => + } + + object Face extends Enumeration with FaceEnum { type Face = Value val BIG_FACE, SMALL_FACE = Value } From 947f0fc0b6e5b7704a96b57d6138a1be8ac3a083 Mon Sep 17 00:00:00 2001 From: ssh Date: Fri, 17 Apr 2020 13:28:09 +0200 Subject: [PATCH 3/4] It works --- src/main/scala/org/example/model/Person.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/scala/org/example/model/Person.scala b/src/main/scala/org/example/model/Person.scala index 6ac9a8f..b1458b0 100644 --- a/src/main/scala/org/example/model/Person.scala +++ b/src/main/scala/org/example/model/Person.scala @@ -12,6 +12,7 @@ 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, From 501b35e4a9f1bfd4e0677accd8420f8a8bc6d566 Mon Sep 17 00:00:00 2001 From: ssh Date: Fri, 17 Apr 2020 13:46:55 +0200 Subject: [PATCH 4/4] looks like it works! --- .../configuration/JacksonConfiguration.scala | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/main/scala/org/example/configuration/JacksonConfiguration.scala b/src/main/scala/org/example/configuration/JacksonConfiguration.scala index 93c2559..bba042d 100644 --- a/src/main/scala/org/example/configuration/JacksonConfiguration.scala +++ b/src/main/scala/org/example/configuration/JacksonConfiguration.scala @@ -5,10 +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 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} +import org.example.model.Person.{Face, FaceEnum, Gender, GenderEnum} import org.example.model.Person.Gender.Gender import org.springframework.context.annotation.{Bean, Configuration} @@ -18,16 +20,16 @@ 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) + val faceSerializerModule = new SimpleModule() + faceSerializerModule.addSerializer(new FaceJsonSerializer()) + faceSerializerModule.addDeserializer(classOf[Face], new FaceJsonDeserializer()) + objectMapper.registerModule(faceSerializerModule) objectMapper } @@ -37,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) @@ -45,7 +47,7 @@ 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()) @@ -54,7 +56,7 @@ object JacksonConfiguration { class FaceJsonSerializer extends StdSerializer[Face](classOf[Face]) { - def this(t: Class[FaceEnum]) = this() + def this(t: FaceEnum) = this() override def serialize(value: Face, gen: JsonGenerator, provider: SerializerProvider): Unit = gen.writeString(value.toString) @@ -62,10 +64,28 @@ object JacksonConfiguration { class FaceJsonDeserializer extends StdDeserializer[Face](classOf[Face]) { - def this(t: Class[FaceEnum]) = this() + 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