Skip to content

Commit

Permalink
switch from Manifest to JavaTypeable
Browse files Browse the repository at this point in the history
Manifest doesn't work in scala3. Switch to using the
JavaTypeable helper from jackson-module-scala.
  • Loading branch information
brharrington committed Apr 10, 2022
1 parent 79caa72 commit 0e8fb59
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller
import akka.stream.Materializer
import akka.util.ByteString
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.module.scala.JavaTypeable
import com.netflix.atlas.json.Json
import com.netflix.spectator.ipc.NetflixHeader

Expand Down Expand Up @@ -86,7 +87,7 @@ object CustomDirectives {
* directly on the directive. It also makes it possible to reuse `parseEntity`
* with a custom function.
*/
def json[T: Manifest]: MediaType => ByteString => T = { mediaType => bs =>
def json[T: JavaTypeable]: MediaType => ByteString => T = { mediaType => bs =>
{
if (isSmile(mediaType))
Json.smileDecode[T](inputStream(bs))
Expand All @@ -101,7 +102,7 @@ object CustomDirectives {
* `application/x-jackson-smile`, then a smile parser will be used. Otherwise
* it will be treated as `application/json` regardless of the content type.
*/
def customJson[T: Manifest](decoder: JsonParser => T): MediaType => ByteString => T = {
def customJson[T: JavaTypeable](decoder: JsonParser => T): MediaType => ByteString => T = {
mediaType => bs =>
{
val p =
Expand Down Expand Up @@ -137,7 +138,7 @@ object CustomDirectives {
* is `application/x-jackson-smile`, then a smile parser will be used. Otherwise
* it will be treated as `application/json` regardless of the content type.
*/
def jsonUnmarshaller[T: Manifest]: FromRequestUnmarshaller[T] = {
def jsonUnmarshaller[T: JavaTypeable]: FromRequestUnmarshaller[T] = {
val parse = json[T]
new FromRequestUnmarshaller[T] {
override def apply(
Expand Down
62 changes: 27 additions & 35 deletions atlas-json/src/main/scala/com/netflix/atlas/json/Json.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import java.io.InputStream
import java.io.OutputStream
import java.io.Reader
import java.io.Writer

import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.core._
import com.fasterxml.jackson.core.json.JsonReadFeature
Expand All @@ -29,10 +28,11 @@ import com.fasterxml.jackson.dataformat.smile.SmileFactory
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.JavaTypeable

object Json {

final class Decoder[T: Manifest](reader: ObjectReader, factory: JsonFactory) {
final class Decoder[T: JavaTypeable](reader: ObjectReader, factory: JsonFactory) {

def decode(json: Array[Byte]): T = decode(factory.createParser(json))

Expand Down Expand Up @@ -139,81 +139,73 @@ object Json {
smileFactory.createParser(bytes, 0, bytes.length)
}

def encode[T: Manifest](obj: T): String = {
def encode[T: JavaTypeable](obj: T): String = {
jsonMapper.writeValueAsString(obj)
}

def encode[T: Manifest](writer: Writer, obj: T): Unit = {
def encode[T: JavaTypeable](writer: Writer, obj: T): Unit = {
jsonMapper.writeValue(writer, obj)
}

def encode[T: Manifest](stream: OutputStream, obj: T): Unit = {
def encode[T: JavaTypeable](stream: OutputStream, obj: T): Unit = {
jsonMapper.writeValue(stream, obj)
}

def encode[T: Manifest](gen: JsonGenerator, obj: T): Unit = {
def encode[T: JavaTypeable](gen: JsonGenerator, obj: T): Unit = {
jsonMapper.writeValue(gen, obj)
}

def decodeResource[T: Manifest](name: String): T = {
def decodeResource[T: JavaTypeable](name: String): T = {
val url = getClass.getClassLoader.getResource(name)
require(url != null, s"could not find resource: $name")
val input = url.openStream()
try decode[T](input)
finally input.close()
}

def decode[T: Manifest](json: Array[Byte]): T = decoder[T].decode(json)
def decode[T: JavaTypeable](json: Array[Byte]): T = decoder[T].decode(json)

def decode[T: Manifest](json: Array[Byte], offset: Int, length: Int): T =
def decode[T: JavaTypeable](json: Array[Byte], offset: Int, length: Int): T =
decoder[T].decode(json, offset, length)

def decode[T: Manifest](json: String): T = decoder[T].decode(json)
def decode[T: JavaTypeable](json: String): T = decoder[T].decode(json)

def decode[T: Manifest](reader: Reader): T = decoder[T].decode(reader)
def decode[T: JavaTypeable](reader: Reader): T = decoder[T].decode(reader)

def decode[T: Manifest](stream: InputStream): T = decoder[T].decode(stream)
def decode[T: JavaTypeable](stream: InputStream): T = decoder[T].decode(stream)

def decode[T: Manifest](node: JsonNode): T = decoder[T].decode(node)
def decode[T: JavaTypeable](node: JsonNode): T = decoder[T].decode(node)

def decode[T: Manifest](parser: JsonParser): T = {
val reader: ObjectReader =
if (manifest.runtimeClass.isArray)
jsonMapper.readerFor(manifest.runtimeClass.asInstanceOf[Class[T]])
else
jsonMapper.readerFor(Reflection.typeReference[T])
def decode[T: JavaTypeable](parser: JsonParser): T = {
val reader = jsonMapper.readerFor(constructType[T](jsonMapper))
val value = reader.readValue[T](parser)
require(parser.nextToken() == null, "invalid json, additional content after value")
value
}

def decoder[T: Manifest]: Decoder[T] = {
val reader: ObjectReader =
if (manifest.runtimeClass.isArray)
jsonMapper.readerFor(manifest.runtimeClass.asInstanceOf[Class[T]])
else
jsonMapper.readerFor(Reflection.typeReference[T])
def decoder[T: JavaTypeable]: Decoder[T] = {
val reader = jsonMapper.readerFor(constructType[T](jsonMapper))
new Decoder[T](reader, jsonFactory)
}

def smileEncode[T: Manifest](obj: T): Array[Byte] = {
def smileEncode[T: JavaTypeable](obj: T): Array[Byte] = {
smileMapper.writeValueAsBytes(obj)
}

def smileEncode[T: Manifest](stream: OutputStream, obj: T): Unit = {
def smileEncode[T: JavaTypeable](stream: OutputStream, obj: T): Unit = {
smileMapper.writeValue(stream, obj)
}

def smileDecode[T: Manifest](stream: InputStream): T = smileDecoder[T].decode(stream)
def smileDecode[T: JavaTypeable](stream: InputStream): T = smileDecoder[T].decode(stream)

def smileDecode[T: Manifest](json: Array[Byte]): T = smileDecoder[T].decode(json)
def smileDecode[T: JavaTypeable](json: Array[Byte]): T = smileDecoder[T].decode(json)

def smileDecoder[T: Manifest]: Decoder[T] = {
val reader: ObjectReader =
if (manifest.runtimeClass.isArray)
jsonMapper.readerFor(manifest.runtimeClass.asInstanceOf[Class[T]])
else
jsonMapper.readerFor(Reflection.typeReference[T])
def smileDecoder[T: JavaTypeable]: Decoder[T] = {
val reader = smileMapper.readerFor(constructType[T](smileMapper))
new Decoder[T](reader, smileFactory)
}

private def constructType[T: JavaTypeable](mapper: ObjectMapper): JavaType = {
implicitly[JavaTypeable[T]].asJavaType(mapper.getTypeFactory)
}
}
50 changes: 0 additions & 50 deletions atlas-json/src/main/scala/com/netflix/atlas/json/Reflection.scala

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.databind.exc.ValueInstantiationException
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.JavaTypeable
import munit.FunSuite

class CaseClassDeserializerSuite extends FunSuite {
Expand All @@ -34,8 +35,9 @@ class CaseClassDeserializerSuite extends FunSuite {
private val mapper = new ObjectMapper()
.registerModule(DefaultScalaModule)

def decode[T: Manifest](json: String): T = {
mapper.readValue[T](json, Reflection.typeReference[T])
def decode[T: JavaTypeable](json: String): T = {
val javaType = implicitly[JavaTypeable[T]].asJavaType(mapper.getTypeFactory)
mapper.readValue[T](json, javaType)
}

test("read simple object") {
Expand Down Expand Up @@ -238,7 +240,7 @@ object CaseClassDeserializerSuite {

case class DeserAnno(@JsonDeserialize(contentAs = classOf[java.lang.Long]) value: Option[Long])

case class DeserUsingAnno(@JsonDeserialize(using = classOf[AddOneDeserializer]) value: Long)
case class DeserUsingAnno(@JsonDeserialize(`using` = classOf[AddOneDeserializer]) value: Long)

class AddOneDeserializer extends JsonDeserializer[java.lang.Long] {

Expand Down

0 comments on commit 0e8fb59

Please sign in to comment.