@@ -2,178 +2,214 @@ package com.fasterxml.jackson
2
2
package module .scala
3
3
package ser
4
4
5
- import util .Implicits ._
6
- import modifiers .OptionTypeModifierModule
7
-
8
- import core .JsonGenerator
9
- import databind ._
10
- import jsontype .TypeSerializer
11
- import jsonschema .{JsonSchema , SchemaAware }
12
- import ser .{ContextualSerializer , BeanPropertyWriter , BeanSerializerModifier , Serializers }
13
- import ser .impl .UnknownSerializer
14
- import ser .std .StdSerializer
15
- import com .fasterxml .jackson .databind .`type` .CollectionLikeType
16
- import jsonFormatVisitors .JsonFormatVisitorWrapper
17
-
18
5
import java .lang .reflect .Type
19
- import java .{util => ju }
6
+ import com .fasterxml .jackson .annotation .JsonInclude
7
+ import com .fasterxml .jackson .core .JsonGenerator
8
+ import com .fasterxml .jackson .databind ._
9
+ import com .fasterxml .jackson .databind .`type` .ReferenceType
10
+ import com .fasterxml .jackson .databind .annotation .JsonSerialize
11
+ import com .fasterxml .jackson .databind .jsonFormatVisitors .JsonFormatVisitorWrapper
12
+ import com .fasterxml .jackson .databind .jsonschema .{JsonSchema , SchemaAware }
13
+ import com .fasterxml .jackson .databind .jsontype .TypeSerializer
14
+ import com .fasterxml .jackson .databind .ser .impl .{PropertySerializerMap , UnknownSerializer }
15
+ import com .fasterxml .jackson .databind .ser .std .StdSerializer
16
+ import com .fasterxml .jackson .databind .ser .{ContextualSerializer , Serializers }
17
+ import com .fasterxml .jackson .databind .util .NameTransformer
18
+ import com .fasterxml .jackson .module .scala .modifiers .OptionTypeModifierModule
19
+ import com .fasterxml .jackson .module .scala .util .Implicits ._
20
+
21
+ private object OptionSerializer {
22
+ def useStatic (provider : SerializerProvider , property : Option [BeanProperty ], referredType : JavaType ): Boolean = {
23
+ // First: no serializer for `Object.class`, must be dynamic
24
+ if (referredType.isJavaLangObject) return false
25
+ // but if type is final, might as well fetch
26
+ if (referredType.isFinal) return true
27
+ // also: if indicated by typing, should be considered static
28
+ if (referredType.useStaticType()) return true
29
+ // if neither, maybe explicit annotation?
30
+ for (
31
+ ann <- property.flatMap(p => Option (p.getMember));
32
+ intr <- Option (provider.getAnnotationIntrospector)
33
+ ) {
34
+ val typing = intr.findSerializationTyping(ann)
35
+ if (typing == JsonSerialize .Typing .STATIC ) return true
36
+ if (typing == JsonSerialize .Typing .DYNAMIC ) return false
37
+ }
38
+ // and finally, may be forced by global static typing (unlikely...)
39
+ provider.isEnabled(MapperFeature .USE_STATIC_TYPING )
40
+ }
41
+
42
+ def findSerializer (provider : SerializerProvider , typ : Class [_], prop : Option [BeanProperty ]): JsonSerializer [AnyRef ] = {
43
+ // Important: ask for TYPED serializer, in case polymorphic handling is needed!
44
+ provider.findTypedValueSerializer(typ, true , prop.orNull)
45
+ }
46
+
47
+ def findSerializer (provider : SerializerProvider , typ : JavaType , prop : Option [BeanProperty ]): JsonSerializer [AnyRef ] = {
48
+ // Important: ask for TYPED serializer, in case polymorphic handling is needed!
49
+ provider.findTypedValueSerializer(typ, true , prop.orNull)
50
+ }
51
+
52
+ def hasContentTypeAnnotation (provider : SerializerProvider , property : BeanProperty ): Boolean = {
53
+ val intr = provider.getAnnotationIntrospector
54
+ if (property == null || intr == null ) return false
55
+ intr.refineSerializationType(provider.getConfig, property.getMember, property.getType) != null
56
+ }
57
+ }
20
58
21
- private class OptionSerializer (elementType : Option [JavaType ],
59
+ private class OptionSerializer (referredType : JavaType ,
60
+ property : Option [BeanProperty ],
22
61
valueTypeSerializer : Option [TypeSerializer ],
23
- beanProperty : Option [BeanProperty ],
24
- elementSerializer : Option [JsonSerializer [ AnyRef ]])
25
- extends StdSerializer [ Option [AnyRef ]]( classOf [ Option [ AnyRef ]])
26
- with ContextualSerializer
27
- with SchemaAware
28
- {
29
- override def serialize ( value : Option [ AnyRef ], jgen : JsonGenerator , provider : SerializerProvider ) {
30
- def doSerialize ( v : AnyRef ) = {
31
- val cls = v.getClass
32
- val ser = elementType.filter(_.hasGenericTypes) match {
33
- case Some (et) => provider.findValueSerializer(provider.constructSpecializedType(et, cls), beanProperty.orNull)
34
- case None => provider.findValueSerializer(cls, beanProperty.orNull )
35
- }
36
- ser.serialize(v, jgen, provider )
37
- }
62
+ valueSerializer : Option [JsonSerializer [ AnyRef ] ],
63
+ contentInclusion : Option [JsonInclude . Include ],
64
+ unwrapper : Option [NameTransformer ],
65
+ var dynamicSerializers : PropertySerializerMap = PropertySerializerMap .emptyForProperties())
66
+ extends StdSerializer [ Option [ AnyRef ]](referredType)
67
+ with ContextualSerializer
68
+ with SchemaAware {
69
+
70
+ import OptionSerializer . _
71
+
72
+ override def unwrappingSerializer ( transformer : NameTransformer ) : JsonSerializer [ Option [ AnyRef ]] = {
73
+ val ser = valueSerializer.map(_.unwrappingSerializer(transformer) )
74
+ val unt = unwrapper.map( NameTransformer .chainedTransformer(transformer, _)).getOrElse(transformer)
75
+ withResolved(property, valueTypeSerializer, ser, Option (unt), contentInclusion )
76
+ }
38
77
39
- valueTypeSerializer match {
40
- case Some (vt) => serializeWithType(value, jgen, provider, vt)
41
- case None =>
42
- (value, elementSerializer) match {
43
- case (Some (v), Some (vs : UnknownSerializer )) => doSerialize(v)
44
- case (Some (v), None ) => doSerialize(v)
45
- case (Some (v), Some (vs)) => vs.serialize(v, jgen, provider)
46
- case (None , _) => provider.defaultSerializeNull(jgen)
47
- }
48
- }
78
+ protected [this ] def withResolved (prop : Option [BeanProperty ], vts : Option [TypeSerializer ],
79
+ valueSer : Option [JsonSerializer [AnyRef ]], unt : Option [NameTransformer ],
80
+ contentIncl : Option [JsonInclude .Include ]): OptionSerializer = {
81
+ if (prop == property && vts == valueTypeSerializer && valueSer == valueSerializer &&
82
+ contentIncl == contentInclusion && unt == unwrapper) this
83
+ else new OptionSerializer (referredType, prop, vts, valueSer, contentIncl, unt, dynamicSerializers)
49
84
}
50
85
51
- override def serializeWithType (value : Option [AnyRef ], jgen : JsonGenerator , provider : SerializerProvider , typeSer : TypeSerializer ) {
52
- def doSerialize (v : AnyRef ) = {
53
- val cls = v.getClass
54
- val ser = elementType.filter(_.hasGenericTypes) match {
55
- case Some (et) => provider.findTypedValueSerializer(provider.constructSpecializedType(et, cls), true , beanProperty.orNull)
56
- case None => provider.findTypedValueSerializer(cls, true , beanProperty.orNull)
57
- }
58
- ser.serializeWithType(v, jgen, provider, typeSer)
86
+ override def createContextual (prov : SerializerProvider , prop : BeanProperty ): JsonSerializer [_] = {
87
+ val propOpt = Option (prop)
88
+
89
+ val vts = valueTypeSerializer.optMap(_.forProperty(prop))
90
+ var ser = for (
91
+ prop <- propOpt;
92
+ member <- Option (prop.getMember);
93
+ serDef <- Option (prov.getAnnotationIntrospector.findContentSerializer(member))
94
+ ) yield prov.serializerInstance(member, serDef)
95
+ ser = ser.orElse(valueSerializer).map(prov.handlePrimaryContextualization(_, prop)).asInstanceOf [Option [JsonSerializer [AnyRef ]]]
96
+ ser = Option (findConvertingContentSerializer(prov, prop, ser.orNull).asInstanceOf [JsonSerializer [AnyRef ]])
97
+ ser = ser match {
98
+ case None => if (hasContentTypeAnnotation(prov, prop)) {
99
+ Option (prov.findValueSerializer(referredType, prop)).filterNot(_.isInstanceOf [UnknownSerializer ])
100
+ } else None
101
+ // Why secondary why not primary?
102
+ case Some (s) => Option (prov.handleSecondaryContextualization(s, prop).asInstanceOf [JsonSerializer [AnyRef ]])
59
103
}
60
104
61
- (value, elementSerializer) match {
62
- case (Some (v), Some (vs : UnknownSerializer )) => doSerialize(v)
63
- case (Some (v), None ) => doSerialize(v)
64
- case (Some (v), Some (vs)) => vs.serializeWithType(v, jgen, provider, typeSer)
65
- case (None , _) => provider.defaultSerializeNull(jgen)
105
+ // A few conditions needed to be able to fetch serializer here:
106
+ if (ser.isEmpty && useStatic(prov, propOpt, referredType)) {
107
+ ser = Option (findSerializer(prov, referredType, propOpt))
66
108
}
109
+ // Also: may want to have more refined exclusion based on referenced value
110
+ val newIncl = propOpt match {
111
+ case None => contentInclusion
112
+ case Some (p) =>
113
+ val pinc = p.findPropertyInclusion(prov.getConfig, classOf [Option [AnyRef ]])
114
+ val incl = pinc.getContentInclusion
115
+ if (incl != JsonInclude .Include .USE_DEFAULTS ) {
116
+ Some (incl)
117
+ } else contentInclusion
118
+ }
119
+ withResolved(propOpt, vts, ser, unwrapper, newIncl)
120
+ }
121
+
122
+ override def isEmpty (provider : SerializerProvider , value : Option [AnyRef ]): Boolean = {
123
+ if (value == null || value.isEmpty) return true
124
+ if (contentInclusion.isEmpty) return false
125
+ val contents = value.get
126
+ valueSerializer
127
+ .getOrElse(findCachedSerializer(provider, contents.getClass))
128
+ .isEmpty(provider, contents)
67
129
}
68
130
69
- override def createContextual (prov : SerializerProvider , property : BeanProperty ): JsonSerializer [_] = {
70
- // Based on the version in AsArraySerializerBase
71
- val typeSer = valueTypeSerializer.optMap(_.forProperty(property))
72
- var ser : Option [JsonSerializer [_]] =
73
- Option (property).flatMap { p =>
74
- Option (p.getMember).flatMap { m =>
75
- Option (prov.getAnnotationIntrospector.findContentSerializer(m)).map { serDef =>
76
- prov.serializerInstance(m, serDef)
77
- }
78
- }
79
- } orElse elementSerializer
80
- ser = Option (findConvertingContentSerializer(prov, property, ser.orNull))
81
- if (ser.isEmpty) {
82
- if (elementType.isDefined) {
83
- if (hasContentTypeAnnotation(prov, property)) {
84
- ser = Option (prov.findValueSerializer(elementType.get, property))
85
- }
131
+ override def isUnwrappingSerializer : Boolean = unwrapper.isDefined
132
+
133
+ override def serialize (opt : Option [AnyRef ], gen : JsonGenerator , provider : SerializerProvider ): Unit = {
134
+ if (opt.isEmpty) {
135
+ if (unwrapper.isEmpty) {
136
+ provider.defaultSerializeNull(gen)
86
137
}
138
+ return
87
139
}
88
- else {
89
- ser = Option (prov.handleSecondaryContextualization(ser.get, property))
140
+
141
+ val value = opt.get
142
+ val ser = valueSerializer.getOrElse(findCachedSerializer(provider, value.getClass))
143
+ valueTypeSerializer match {
144
+ case Some (vts) => ser.serializeWithType(value, gen, provider, vts)
145
+ case None => ser.serialize(value, gen, provider)
90
146
}
91
- if ((ser != elementSerializer) || (property != beanProperty.orNull) || (valueTypeSerializer != typeSer))
92
- new OptionSerializer (elementType, typeSer, Option (property), ser.asInstanceOf [Option [JsonSerializer [AnyRef ]]])
93
- else this
94
147
}
95
148
96
- def hasContentTypeAnnotation ( provider : SerializerProvider , property : BeanProperty ) = {
97
- Option (property).exists { p =>
98
- Option (provider.getAnnotationIntrospector).exists { intr =>
99
- Option (intr.refineSerializationType( provider.getConfig, p.getMember, p.getType)).isDefined
149
+ override def serializeWithType ( opt : Option [ AnyRef ], gen : JsonGenerator , provider : SerializerProvider , typeSer : TypeSerializer ) : Unit = {
150
+ if (opt.isEmpty) {
151
+ if (unwrapper.isEmpty) {
152
+ provider.defaultSerializeNull(gen)
100
153
}
154
+ return
101
155
}
156
+ // Otherwise apply type-prefix/suffix, then std serialize:
157
+ typeSer.writeTypePrefixForScalar(opt, gen, classOf [Option [_]])
158
+ serialize(opt, gen, provider)
159
+ typeSer.writeTypeSuffixForScalar(opt, gen)
102
160
}
103
161
104
- override def isEmpty (value : Option [AnyRef ]): Boolean = value.isEmpty
105
-
106
162
override def getSchema (provider : SerializerProvider , typeHint : Type ): JsonNode =
107
163
getSchema(provider, typeHint, isOptional = true )
108
164
109
165
override def getSchema (provider : SerializerProvider , typeHint : Type , isOptional : Boolean ): JsonNode = {
110
- val contentSerializer = elementSerializer .getOrElse {
166
+ val contentSerializer = valueSerializer .getOrElse {
111
167
val javaType = provider.constructType(typeHint)
112
168
val componentType = javaType.getContentType
113
- provider.findTypedValueSerializer(componentType, true , beanProperty .orNull)
169
+ provider.findTypedValueSerializer(componentType, true , property .orNull)
114
170
}
115
171
contentSerializer match {
116
172
case cs : SchemaAware => cs.getSchema(provider, contentSerializer.handledType(), isOptional)
117
173
case _ => JsonSchema .getDefaultSchemaNode
118
174
}
119
175
}
120
176
121
- override def acceptJsonFormatVisitor (wrapper : JsonFormatVisitorWrapper , javaType : JavaType ) {
122
- val containedType = javaType.getContentType
123
- val ser = elementSerializer.getOrElse(wrapper.getProvider.findTypedValueSerializer(containedType, true , beanProperty.orNull) )
124
- ser.acceptJsonFormatVisitor(wrapper, containedType )
177
+ override def acceptJsonFormatVisitor (visitor : JsonFormatVisitorWrapper , typeHint : JavaType ): Unit = {
178
+ var ser = valueSerializer.getOrElse(findSerializer(visitor.getProvider, referredType, property))
179
+ ser = unwrapper.map(ser.unwrappingSerializer).getOrElse(ser )
180
+ ser.acceptJsonFormatVisitor(visitor, referredType )
125
181
}
126
- }
127
182
128
- private class OptionPropertyWriter (delegate : BeanPropertyWriter ) extends BeanPropertyWriter (delegate)
129
- {
130
- override def serializeAsField (bean : AnyRef , jgen : JsonGenerator , prov : SerializerProvider ) {
131
- (get(bean), _nullSerializer) match {
132
- // value is None, which we'll serialize as null, but there's no
133
- // null-serializer, which means it should be suppressed
134
- case (None , null ) => return
135
- case _ => super .serializeAsField(bean, jgen, prov)
183
+ protected [this ] def findCachedSerializer (prov : SerializerProvider , typ : Class [_]): JsonSerializer [AnyRef ] = {
184
+ var ser = dynamicSerializers.serializerFor(typ)
185
+ if (ser == null ) {
186
+ ser = findSerializer(prov, typ, property)
187
+ ser = unwrapper.map(ser.unwrappingSerializer).getOrElse(ser)
188
+ dynamicSerializers = dynamicSerializers.newWith(typ, ser)
136
189
}
137
- }
138
- }
139
-
140
- private object OptionBeanSerializerModifier extends BeanSerializerModifier {
141
-
142
- override def changeProperties (config : SerializationConfig ,
143
- beanDesc : BeanDescription ,
144
- beanProperties : ju.List [BeanPropertyWriter ]): ju.List [BeanPropertyWriter ] = {
145
- import scala .collection .JavaConversions ._
146
- beanProperties.transform(w => if (isOption(w)) new OptionPropertyWriter (w) else w)
147
- }
148
-
149
- private [this ] def isOption (writer : BeanPropertyWriter ): Boolean = {
150
- classOf [Option [_]].isAssignableFrom(writer.getPropertyType)
190
+ ser
151
191
}
152
192
}
153
193
154
194
private object OptionSerializerResolver extends Serializers .Base {
155
195
156
196
private val OPTION = classOf [Option [_]]
157
197
158
- override def findCollectionLikeSerializer (config : SerializationConfig ,
159
- `type` : CollectionLikeType ,
160
- beanDesc : BeanDescription ,
161
- elementTypeSerializer : TypeSerializer ,
162
- elementValueSerializer : JsonSerializer [AnyRef ]
163
- ): JsonSerializer [_] =
164
-
165
- if (! OPTION .isAssignableFrom(`type`.getRawClass)) null
166
- else {
167
- val elementType = `type`.getContentType
168
- val typeSer = Option (elementTypeSerializer).orElse(Option (elementType.getTypeHandler.asInstanceOf [TypeSerializer ]))
169
- val valSer = Option (elementValueSerializer).orElse(Option (elementType.getValueHandler.asInstanceOf [JsonSerializer [AnyRef ]]))
170
- new OptionSerializer (Option (`type`.getContentType), typeSer, None , valSer)
171
- }
198
+ override def findReferenceSerializer (config : SerializationConfig ,
199
+ refType : ReferenceType ,
200
+ beanDesc : BeanDescription ,
201
+ contentTypeSerializer : TypeSerializer ,
202
+ contentValueSerializer : JsonSerializer [AnyRef ]): JsonSerializer [_] = {
203
+ if (! OPTION .isAssignableFrom(refType.getRawClass)) return null
204
+ new OptionSerializer (refType.getReferencedType, property = None ,
205
+ valueTypeSerializer = Option (contentTypeSerializer).orElse(Option (refType.getTypeHandler[TypeSerializer ])),
206
+ valueSerializer = Option (contentValueSerializer).orElse(Option (refType.getValueHandler[JsonSerializer [AnyRef ]])),
207
+ contentInclusion = None , unwrapper = None )
208
+ }
172
209
}
173
210
174
211
trait OptionSerializerModule extends OptionTypeModifierModule {
175
212
this += { ctx =>
176
213
ctx addSerializers OptionSerializerResolver
177
- ctx addBeanSerializerModifier OptionBeanSerializerModifier
178
214
}
179
215
}
0 commit comments