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

com.fasterxml.jackson.module.scala.ser.JacksonOptionSerializer.scala Maven / Gradle / Ivy

package com.fasterxml.jackson
package module.scala
package ser

import annotation.JsonTypeInfo
import annotation.JsonTypeName
import core.JsonGenerator
import databind.BeanDescription
import databind.BeanProperty
import databind.JavaType
import databind.JsonNode
import databind.JsonSerializer
import databind.SerializationConfig
import databind.SerializerProvider
import databind.`type`.CollectionLikeType
import databind.jsonFormatVisitors.JsonFormatVisitorWrapper
import databind.jsonschema.JsonSchema
import databind.jsonschema.SchemaAware
import databind.jsontype.TypeSerializer
import databind.ser.BeanPropertyWriter
import databind.ser.BeanSerializer
import databind.ser.BeanSerializerModifier
import databind.ser.ContextualSerializer
import databind.ser.Serializers
import databind.ser.std.BeanSerializerBase
import databind.ser.std.StdSerializer
import deser.OptionDeserializerModule
import deser.UntypedObjectDeserializerModule
import introspect.ScalaClassIntrospectorModule
import modifiers.OptionTypeModifierModule
import util.Implicits.mkOptionW

import java.lang.reflect.Type
import scala.collection.JavaConverters._

private class CustomBeanSerializer(bsb: BeanSerializerBase) extends BeanSerializer(bsb) {

  def serializeWithType(
    bean: Any,
    jgen: JsonGenerator,
    provider: SerializerProvider,
    jti: JsonTypeInfo,
    jtn: JsonTypeName) {
    jgen.writeStartObject()
    jgen.writeStringField(jti.property, jtn.value)
    if (_propertyFilterId != null) {
      serializeFieldsFiltered(bean, jgen, provider);
    } else {
      serializeFields(bean, jgen, provider);
    }
    jgen.writeEndObject()
  }
}

private class CustomOptionSerializer(
  elementType: Option[JavaType],
  valueTypeSerializer: Option[TypeSerializer],
  beanProperty: Option[BeanProperty],
  elementSerializer: Option[JsonSerializer[AnyRef]],
  jsonTypeInfo: Option[JsonTypeInfo])
    extends StdSerializer[Option[_]](classOf[Option[_]])
    with ContextualSerializer
    with SchemaAware {
  def serialize(value: Option[_], jgen: JsonGenerator, provider: SerializerProvider) {
    (value, elementSerializer) match {
      case (Some(v: AnyRef), Some(vs)) =>
        vs.serialize(v, jgen, provider)
      case (Some(v), _) =>
        jsonTypeInfo match {
          case Some(jti) =>
            val clazz = v.getClass
            val jtn = clazz.getAnnotation(classOf[JsonTypeName])
            if (jtn != null) {
              beanProperty match {
                case Some(bp) =>
                  provider.findValueSerializer(clazz, bp) match {
                    case bs: BeanSerializer =>
                      var cbs = new CustomBeanSerializer(bs)
                      cbs.serializeWithType(v, jgen, provider, jti, jtn)
                    case vs =>
                      vs.serialize(v.asInstanceOf[AnyRef], jgen, provider)
                  }
                case _ =>
                  provider.defaultSerializeValue(v, jgen)
              }
            } else {
              provider.defaultSerializeValue(v, jgen)
            }
          case _ =>
            provider.defaultSerializeValue(v, jgen)
        }
      case (None, _) =>
        provider.defaultSerializeNull(jgen)
    }
  }

  def createContextual(prov: SerializerProvider, property: BeanProperty): JsonSerializer[_] = {
    // Based on the version in AsArraySerializerBase
    val typeSer = valueTypeSerializer.optMap(_.forProperty(property))
    var ser: Option[JsonSerializer[_]] =
      Option(property).flatMap { p =>
        Option(p.getMember).flatMap { m =>
          Option(prov.getAnnotationIntrospector.findContentSerializer(m)).map { serDef =>
            prov.serializerInstance(m, serDef)
          }
        }
      } orElse elementSerializer
    ser = Option(findConvertingContentSerializer(prov, property, ser.orNull))
    if (ser.isEmpty) {
      if (elementType.isDefined) {
        if (hasContentTypeAnnotation(prov, property)) {
          ser = Option(prov.findValueSerializer(elementType.get, property))
        }
      }
    } else {
      ser = Option(prov.handleSecondaryContextualization(ser.get, property))
    }
    if ((ser != elementSerializer) || (property != beanProperty.orNull) || (valueTypeSerializer != typeSer)) {
      val jti = property.getAnnotation(classOf[JsonTypeInfo])
      new CustomOptionSerializer(elementType, typeSer, Option(property), ser.asInstanceOf[Option[JsonSerializer[AnyRef]]], Option(jti))
    } else this
  }

  def hasContentTypeAnnotation(provider: SerializerProvider, property: BeanProperty) = {
    Option(property).exists { p =>
      Option(provider.getAnnotationIntrospector).exists { intr =>
        Option(intr.findSerializationContentType(p.getMember, p.getType)).isDefined
      }
    }
  }

  override def isEmpty(value: Option[_]): Boolean = value.isEmpty

  override def getSchema(provider: SerializerProvider, typeHint: Type): JsonNode =
    getSchema(provider, typeHint, isOptional = true)

  override def getSchema(provider: SerializerProvider, typeHint: Type, isOptional: Boolean): JsonNode = {
    val contentSerializer = elementSerializer.getOrElse {
      val javaType = provider.constructType(typeHint)
      val componentType = javaType.containedType(0)
      provider.findTypedValueSerializer(componentType, true, beanProperty.orNull)
    }
    contentSerializer match {
      case cs: SchemaAware => cs.getSchema(provider, contentSerializer.handledType(), isOptional)
      case _               => JsonSchema.getDefaultSchemaNode
    }
  }

  override def acceptJsonFormatVisitor(wrapper: JsonFormatVisitorWrapper, javaType: JavaType) {
    val containedType = javaType.containedType(0)
    val ser = elementSerializer.getOrElse(wrapper.getProvider.findTypedValueSerializer(containedType, true, beanProperty.orNull))
    ser.acceptJsonFormatVisitor(wrapper, containedType)
  }
}

private class CustomOptionPropertyWriter(delegate: BeanPropertyWriter) extends BeanPropertyWriter(delegate) {
  override def serializeAsField(bean: AnyRef, jgen: JsonGenerator, prov: SerializerProvider) {
    (get(bean), _nullSerializer) match {
      // value is None, which we'll serialize as null, but there's no
      // null-serializer, which means it should be suppressed
      case (None, null) =>
      case _            => super.serializeAsField(bean, jgen, prov)
    }
  }
}

private object CustomOptionBeanSerializerModifier extends BeanSerializerModifier {

  override def changeProperties(config: SerializationConfig,
    beanDesc: BeanDescription,
    beanProperties: java.util.List[BeanPropertyWriter]): java.util.List[BeanPropertyWriter] = {

    beanProperties.asScala.transform { w =>
      if (classOf[Option[_]].isAssignableFrom(w.getPropertyType))
        new CustomOptionPropertyWriter(w)
      else
        w
    }.asJava

  }
}

private object CustomOptionSerializerResolver extends Serializers.Base {

  private val OPTION = classOf[Option[_]]

  override def findCollectionLikeSerializer(config: SerializationConfig,
    `type`: CollectionLikeType,
    beanDesc: BeanDescription,
    elementTypeSerializer: TypeSerializer,
    elementValueSerializer: JsonSerializer[AnyRef]): JsonSerializer[_] =

    if (!OPTION.isAssignableFrom(`type`.getRawClass)) null
    else new CustomOptionSerializer(Option(`type`.containedType(0)), Option(elementTypeSerializer), None, Option(elementValueSerializer), None)
}

trait CustomOptionSerializerModule extends OptionTypeModifierModule {
  this += { ctx =>
    ctx addSerializers CustomOptionSerializerResolver
    ctx addBeanSerializerModifier CustomOptionBeanSerializerModifier
  }
}

trait CustomOptionModule extends CustomOptionSerializerModule with OptionDeserializerModule

class CustomDefaultScalaModule
    extends JacksonModule
    with IteratorModule
    with EnumerationModule
    with CustomOptionModule
    with SeqModule
    with IterableModule
    with TupleModule
    with MapModule
    with SetModule
    with ScalaClassIntrospectorModule
    with UntypedObjectDeserializerModule {
  override def getModuleName = "CustomDefaultScalaModule"
}

object CustomDefaultScalaModule extends CustomDefaultScalaModule




© 2015 - 2025 Weber Informatics LLC | Privacy Policy