Skip to content

Commit

Permalink
backport #439 - jackson-databind 3.0.0-SNAPSHOT are causing compile p…
Browse files Browse the repository at this point in the history
…roblems though
  • Loading branch information
jroper authored and PJ Fanning committed Dec 10, 2019
1 parent 73bcfed commit 22229b7
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 6 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ unmanagedSourceDirectories in Compile += {
}

val jacksonVersion = "3.0.0-SNAPSHOT"
val jacksonLegacyVersion = "2.10.0"
val jacksonLegacyVersion = "2.10.1"

libraryDependencies ++= Seq(
"com.fasterxml.jackson.core" % "jackson-core" % jacksonVersion,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.fasterxml.jackson.module.scala.deser

import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.{DeserializationConfig, JavaType}
import com.fasterxml.jackson.module.scala.modifiers.ScalaTypeModifierModule
import com.fasterxml.jackson.module.scala.util.FactorySorter

Expand Down Expand Up @@ -46,5 +46,10 @@ trait SeqDeserializerModule extends ScalaTypeModifierModule {
super.builderFor[A](cls, valueType)
}
}

override def hasDeserializerFor(config: DeserializationConfig, valueType: Class[_]): Boolean = {
// TODO add implementation
???
}
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,30 @@ object BeanIntrospector {
val primaryConstructor = c.getConstructors.headOption
val debugCtorParamNames = primaryConstructor.toIndexedSeq.flatMap(getCtorParams)
val index = debugCtorParamNames.indexOf(name)
val companion = findCompanionObject(c)
if (index >= 0) {
Some(ConstructorParameter(primaryConstructor.get, index, None))
Some(ConstructorParameter(primaryConstructor.get, index, findConstructorDefaultValue(companion, index)))
} else {
findConstructorParam(c.getSuperclass, name)
}
}

def findConstructorDefaultValue(maybeCompanion: Option[AnyRef], index: Int): Option[() => AnyRef] = {
val methodName = "$lessinit$greater$default$" + (index + 1)
maybeCompanion.flatMap(companion => companion.getClass.getMethods.toStream.collectFirst {
case method if method.getName == methodName && method.getParameterTypes.length == 0 =>
() => method.invoke(companion)
})
}

def findCompanionObject(c: Class[_]): Option[AnyRef] = {
try {
Some(c.getClassLoader.loadClass(c.getName + "$").getDeclaredField("MODULE$").get(null))
} catch {
case e: Exception => None
}
}

val hierarchy: Seq[Class[_]] = {
@tailrec
def next(c: Class[_], acc: List[Class[_]]): List[Class[_]] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.fasterxml.jackson.module.scala.util.Implicits._

import scala.language.existentials

case class ConstructorParameter(constructor: Constructor[_], index: Int, defaultValueMethod: Option[Method])
case class ConstructorParameter(constructor: Constructor[_], index: Int, defaultValue: Option[() => AnyRef])

case class PropertyDescriptor(name: String,
param: Option[ConstructorParameter],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ package com.fasterxml.jackson.module.scala.introspect
import java.lang.annotation.Annotation

import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.databind.{BeanDescription, DeserializationConfig, DeserializationContext}
import com.fasterxml.jackson.databind.`type`.ClassKey
import com.fasterxml.jackson.databind.deser.{CreatorProperty, NullValueProvider, SettableBeanProperty, ValueInstantiator, ValueInstantiators}
import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator
import com.fasterxml.jackson.databind.introspect._
import com.fasterxml.jackson.databind.util.SimpleLookupCache
import com.fasterxml.jackson.databind.util.{AccessPattern, SimpleLookupCache}
import com.fasterxml.jackson.module.paranamer.ParanamerAnnotationIntrospector
import com.fasterxml.jackson.module.scala.JacksonModule
import com.fasterxml.jackson.module.scala.util.Implicits._

object ScalaAnnotationIntrospector extends NopAnnotationIntrospector
object ScalaAnnotationIntrospector extends NopAnnotationIntrospector with ValueInstantiators
{
private [this] val _descriptorCache = new SimpleLookupCache[ClassKey, BeanDescriptor](16, 100)

Expand Down Expand Up @@ -114,9 +117,54 @@ object ScalaAnnotationIntrospector extends NopAnnotationIntrospector
JsonCreator.Mode.PROPERTIES
} else null
}

class ScalaValueInstantiator(delegate: StdValueInstantiator, config: DeserializationConfig, descriptor: BeanDescriptor) extends StdValueInstantiator(delegate) {

private val overriddenConstructorArguments = {
val args = delegate.getFromObjectArguments(None.orNull)

args.map {
case creator: CreatorProperty =>
// Locate the constructor param that matches it
descriptor.properties.find(_.param.exists(_.index == creator.getCreatorIndex)) match {
case Some(PropertyDescriptor(name, Some(ConstructorParameter(_, _, Some(defaultValue))), _, _, _, _, _)) =>
creator.withNullProvider(new NullValueProvider {
override def getNullValue(ctxt: DeserializationContext): AnyRef = defaultValue()

override def getNullAccessPattern: AccessPattern = AccessPattern.DYNAMIC
})
case _ => creator
}
case other => other
}
}

override def getFromObjectArguments(context: DeserializationContext): Array[SettableBeanProperty] = {
overriddenConstructorArguments
}
}

override def findValueInstantiator(config: DeserializationConfig, beanDesc: BeanDescription,
defaultInstantiator: ValueInstantiator): ValueInstantiator = {

if (isMaybeScalaBeanType(beanDesc.getBeanClass)) {

val descriptor = _descriptorFor(beanDesc.getBeanClass)
if (descriptor.properties.exists(_.param.exists(_.defaultValue.isDefined))) {
defaultInstantiator match {
case std: StdValueInstantiator =>
new ScalaValueInstantiator(std, config, descriptor)
case other =>
throw new IllegalArgumentException("Cannot customise a non StdValueInstantiatiator: " + other.getClass)
}
} else defaultInstantiator

} else defaultInstantiator
}
}

trait ScalaAnnotationIntrospectorModule extends JacksonModule {
this += { _.appendAnnotationIntrospector(new ParanamerAnnotationIntrospector()) }
this += { _.appendAnnotationIntrospector(ScalaAnnotationIntrospector) }
this += { _.addValueInstantiators(ScalaAnnotationIntrospector) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ object CreatorTest
this(script, 0)
}
}

case class ConstructorWithDefaultValues(s: String = "some string", i: Int = 10, dummy: String)
}


@RunWith(classOf[JUnitRunner])
class CreatorTest extends DeserializationFixture {
import CreatorTest._
Expand Down Expand Up @@ -104,4 +107,14 @@ class CreatorTest extends DeserializationFixture {
val roundTrip = f.readValue[MultipleConstructorsAnn](bean)
roundTrip shouldEqual orig
}

it should "support default values" in { f =>
val deser = f.readValue[ConstructorWithDefaultValues]("""{}""")
deser.s shouldEqual "some string"
deser.i shouldEqual 10
deser.dummy shouldEqual null
val deser2 = f.readValue[ConstructorWithDefaultValues]("""{"s":"passed","i":5}""")
deser2.s shouldEqual "passed"
deser2.i shouldEqual 5
}
}

0 comments on commit 22229b7

Please sign in to comment.