All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fasterxml.jackson.module.scala.deser.EitherDeserializer.scala Maven / Gradle / Ivy

The newest version!
package com.fasterxml.jackson.module.scala.deser

import com.fasterxml.jackson.core.{JsonParser, JsonToken}
import com.fasterxml.jackson.databind._
import com.fasterxml.jackson.databind.`type`.ReferenceType
import com.fasterxml.jackson.databind.deser._
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer
import com.fasterxml.jackson.module.scala.JacksonModule
import com.fasterxml.jackson.module.scala.deser.EitherDeserializer.ElementDeserializerConfig

private class EitherDeserializer(javaType: JavaType,
                                 config: DeserializationConfig,
                                 leftDeserializerConfig: ElementDeserializerConfig,
                                 rightDeserializerConfig: ElementDeserializerConfig)
  extends StdDeserializer[Either[AnyRef, AnyRef]](classOf[Either[AnyRef, AnyRef]])
  with ContextualDeserializer {

  override def createContextual(ctxt: DeserializationContext, property: BeanProperty): JsonDeserializer[Either[AnyRef, AnyRef]] = {

    def deserializerConfigFor(param: Int, property: BeanProperty): ElementDeserializerConfig = {
      val containedType = javaType.containedType(param)

      val paramDeserializer = Option( ctxt.findContextualValueDeserializer(containedType, property) )
      val typeDeserializer = Option(property).flatMap(p => Option(BeanDeserializerFactory.instance.findPropertyTypeDeserializer(ctxt.getConfig, containedType, p.getMember)) )

      ElementDeserializerConfig( paramDeserializer, typeDeserializer )
    }

    javaType.containedTypeCount match {
      case 2 =>
        val leftDeserializerConfig = deserializerConfigFor(0, property)
        val rightDeserializerConfig = deserializerConfigFor(1, property)
        new EitherDeserializer(javaType, config, leftDeserializerConfig, rightDeserializerConfig)
      case 1 =>
        if (javaType.getBindings.getBoundName(0) == "A") {
          val leftDeserializerConfig = deserializerConfigFor(0, property)
          new EitherDeserializer(javaType, config, leftDeserializerConfig, ElementDeserializerConfig.empty)
        } else {
          val rightDeserializerConfig = deserializerConfigFor(0, property)
          new EitherDeserializer(javaType, config, ElementDeserializerConfig.empty, rightDeserializerConfig)
        }
      case _ => this
    }
  }

  private def deserializeValue(`type`: JsonToken, config: ElementDeserializerConfig, jp: JsonParser, ctxt: DeserializationContext) =
    (config, `type`) match {
      case (ElementDeserializerConfig(Some(ed), _), JsonToken.VALUE_NULL) if ed.isInstanceOf[OptionDeserializer] =>
        None
      case (_, JsonToken.VALUE_NULL) => None.orNull
      case (ElementDeserializerConfig(Some(ed), Some(td)), _) =>
        ed.deserializeWithType(jp, ctxt, td)
      case (ElementDeserializerConfig(Some(ed), _), _) => ed.deserialize(jp, ctxt)
      case (_, _) => ctxt.handleUnexpectedToken(javaType.getRawClass, jp)
    }

  private def deserializeEither(jp: JsonParser, ctxt: DeserializationContext): Either[AnyRef, AnyRef] = {
    jp.currentToken() match {
      case JsonToken.START_OBJECT =>
        val key = jp.nextFieldName()
        val `type` = jp.nextToken()

        val result = key match {
          case "l" => Left(deserializeValue(`type`, leftDeserializerConfig, jp, ctxt))
          case "left" => Left(deserializeValue(`type`, leftDeserializerConfig, jp, ctxt))
          case "r" => Right(deserializeValue(`type`, rightDeserializerConfig, jp, ctxt))
          case "right" => Right(deserializeValue(`type`, rightDeserializerConfig, jp, ctxt))
          case _ => ctxt.handleUnexpectedToken(javaType, jp).asInstanceOf[Either[AnyRef, AnyRef]]
        }

        // consume END_OBJECT
        jp.nextToken()

        result
      case JsonToken.START_ARRAY =>
        val key = jp.nextTextValue()
        val `type` = jp.nextToken()

        val result = key match {
          case "l" => Left(deserializeValue(`type`, leftDeserializerConfig, jp, ctxt))
          case "left" => Left(deserializeValue(`type`, leftDeserializerConfig, jp, ctxt))
          case "r" => Right(deserializeValue(`type`, rightDeserializerConfig, jp, ctxt))
          case "right" => Right(deserializeValue(`type`, rightDeserializerConfig, jp, ctxt))
          case _ => ctxt.handleUnexpectedToken(javaType, jp).asInstanceOf[Either[AnyRef, AnyRef]]
        }

        // consume END_ARRAY
        jp.nextToken()

        result
      case _ => ctxt.handleUnexpectedToken(javaType, jp).asInstanceOf[Either[AnyRef, AnyRef]]
    }
  }

  override def deserialize(jp: JsonParser, ctxt: DeserializationContext): Either[AnyRef, AnyRef] =
    deserializeEither(jp, ctxt)
  override def deserializeWithType(jp: JsonParser, ctxt: DeserializationContext, typeDeserializer: TypeDeserializer): Either[AnyRef, AnyRef] =
    deserializeEither(jp, ctxt)
}

private object EitherDeserializer {
  case class ElementDeserializerConfig(deserializer: Option[JsonDeserializer[AnyRef]], typeDeserializer: Option[TypeDeserializer])

  object ElementDeserializerConfig {
    val empty = ElementDeserializerConfig(None, None)
  }
}


private object EitherDeserializerResolver extends Deserializers.Base {

  private val EITHER = classOf[Either[_, _]]

  override def findBeanDeserializer(`type`: JavaType, config: DeserializationConfig, beanDesc: BeanDescription): JsonDeserializer[_] = {
    val rawClass = `type`.getRawClass

    if (!EITHER.isAssignableFrom(rawClass)) {
      super.findBeanDeserializer(`type`, config, beanDesc)
    } else {
      new EitherDeserializer( `type`, config, ElementDeserializerConfig.empty, ElementDeserializerConfig.empty )
    }
  }

  override def findReferenceDeserializer(refType: ReferenceType, config: DeserializationConfig,
                                         beanDesc: BeanDescription, contentTypeDeserializer: TypeDeserializer,
                                         contentDeserializer: JsonDeserializer[_]): JsonDeserializer[_] = {
    val rawClass = refType.getRawClass

    if (!EITHER.isAssignableFrom(rawClass)) {
      super.findReferenceDeserializer(refType, config, beanDesc, contentTypeDeserializer, contentDeserializer)
    } else {
      new EitherDeserializer( refType, config, ElementDeserializerConfig.empty, ElementDeserializerConfig.empty )
    }
  }
}

trait EitherDeserializerModule extends JacksonModule {
  this += EitherDeserializerResolver
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy