Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
S
spring-boot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DEMO
spring-boot
Commits
bf633fc5
Commit
bf633fc5
authored
Jun 03, 2019
by
Phillip Webb
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Polish "Support JsonComponent key serializers/deserialzers"
See gh-16544
parent
361efc7c
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
97 additions
and
94 deletions
+97
-94
JsonComponent.java
.../java/org/springframework/boot/jackson/JsonComponent.java
+28
-21
JsonComponentModule.java
...org/springframework/boot/jackson/JsonComponentModule.java
+59
-62
JsonComponentModuleTests.java
...pringframework/boot/jackson/JsonComponentModuleTests.java
+3
-3
NameAndAge.java
...est/java/org/springframework/boot/jackson/NameAndAge.java
+3
-4
NameAndAgeJsonKeyComponent.java
...ingframework/boot/jackson/NameAndAgeJsonKeyComponent.java
+2
-2
NameAndCareerJsonComponent.java
...ingframework/boot/jackson/NameAndCareerJsonComponent.java
+2
-2
No files found.
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonComponent.java
View file @
bf633fc5
/*
* Copyright 2012-201
7
the original author or authors.
* Copyright 2012-201
9
the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
...
...
@@ -30,11 +30,10 @@ import org.springframework.core.annotation.AliasFor;
import
org.springframework.stereotype.Component
;
/**
* {@link Component} that provides {@link JsonSerializer} and/or {@link JsonDeserializer}
* implementations to be registered with Jackson when {@link JsonComponentModule} is in
* use. Can be used to annotate {@link JsonSerializer}, {@link JsonDeserializer}, or
* {@link KeyDeserializer} implementations directly or a class that contains them as
* inner-classes. For example: <pre class="code">
* {@link Component} that provides {@link JsonSerializer}, {@link JsonDeserializer} or
* {@link KeyDeserializer} implementations to be registered with Jackson when
* {@link JsonComponentModule} is in use. Can be used to annotate implementations directly
* or a class that contains them as inner-classes. For example: <pre class="code">
* @JsonComponent
* public class CustomerJsonComponent {
*
...
...
@@ -57,6 +56,7 @@ import org.springframework.stereotype.Component;
* @see JsonComponentModule
* @since 1.4.0
* @author Phillip Webb
* @author Paul Aly
*/
@Target
(
ElementType
.
TYPE
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
...
...
@@ -73,33 +73,40 @@ public @interface JsonComponent {
String
value
()
default
""
;
/**
* Indicates whether the component should be registered as a type serializer and/or
* deserializer or a key serializer and/or deserializer.
* @return the component's handle type
* The types that are handled by the provided serializer/deserializer. This attribute
* is mandatory for a {@link KeyDeserializer}, as the type cannot be inferred. For a
* {@link JsonSerializer} or {@link JsonDeserializer} it can be used to limit handling
* to a subclasses of type inferred from the generic.
* @return the types that should be handled by the component
* @since 2.2.0
*/
Handle
handle
()
default
Handle
.
TYPES
;
Class
<?>[]
type
()
default
{}
;
/**
* Specify the classes handled by the serialization and/or deserialization of the
* component. Necessary to be specified for a {@link KeyDeserializer}, as the type
* cannot be inferred. On other types can be used to only handle a subset of
* subclasses.
* @return the classes that should be handled by the component
* The scope under which the serializer/deserializer should be registered with the
* module.
* @return the component's handle type
* @since 2.2.0
*/
Class
<?>[]
handleClasses
()
default
{}
;
Scope
scope
()
default
Scope
.
VALUES
;
/**
* An enumeration of possible handling types for the component.
* The various scopes under which a serializer/deserialzier can be registered.
* @since 2.2.0
*/
enum
Handl
e
{
enum
Scop
e
{
/**
* Register the component as a Type serializer and/or deserializer.
* A serializer/deserializer for regular value content.
* @see JsonSerializer
* @see JsonDeserializer
*/
TYP
ES
,
VALU
ES
,
/**
* Register the component as a Key serializer and/or deserializer.
* A serializer/deserializer for keys.
* @see JsonSerializer
* @see KeyDeserializer
*/
KEYS
...
...
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonComponentModule.java
View file @
bf633fc5
/*
* Copyright 2012-201
8
the original author or authors.
* Copyright 2012-201
9
the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
...
...
@@ -18,6 +18,7 @@ package org.springframework.boot.jackson;
import
java.lang.reflect.Modifier
;
import
java.util.Map
;
import
java.util.function.BiConsumer
;
import
javax.annotation.PostConstruct
;
...
...
@@ -27,13 +28,19 @@ import com.fasterxml.jackson.databind.KeyDeserializer;
import
com.fasterxml.jackson.databind.Module
;
import
com.fasterxml.jackson.databind.module.SimpleModule
;
import
org.springframework.beans.BeanUtils
;
import
org.springframework.beans.BeansException
;
import
org.springframework.beans.factory.BeanFactory
;
import
org.springframework.beans.factory.BeanFactoryAware
;
import
org.springframework.beans.factory.HierarchicalBeanFactory
;
import
org.springframework.beans.factory.ListableBeanFactory
;
import
org.springframework.boot.jackson.JsonComponent.Scope
;
import
org.springframework.core.ResolvableType
;
import
org.springframework.core.annotation.AnnotationUtils
;
import
org.springframework.core.annotation.MergedAnnotation
;
import
org.springframework.core.annotation.MergedAnnotations
;
import
org.springframework.core.annotation.MergedAnnotations.SearchStrategy
;
import
org.springframework.util.Assert
;
import
org.springframework.util.ObjectUtils
;
/**
* Spring Bean and Jackson {@link Module} to register {@link JsonComponent} annotated
...
...
@@ -70,88 +77,78 @@ public class JsonComponentModule extends SimpleModule implements BeanFactoryAwar
Map
<
String
,
Object
>
beans
=
beanFactory
.
getBeansWithAnnotation
(
JsonComponent
.
class
);
for
(
Object
bean
:
beans
.
values
())
{
JsonComponent
annotation
=
AnnotationUtils
.
findAnnotation
(
bean
.
getClass
(),
JsonComponent
.
class
);
addJsonBean
(
bean
,
annotation
);
addJsonBean
(
bean
);
}
}
private
void
addJsonBean
(
Object
bean
,
JsonComponent
annotation
)
{
private
void
addJsonBean
(
Object
bean
)
{
MergedAnnotation
<
JsonComponent
>
annotation
=
MergedAnnotations
.
from
(
bean
.
getClass
(),
SearchStrategy
.
EXHAUSTIVE
)
.
get
(
JsonComponent
.
class
);
Class
<?>[]
types
=
annotation
.
getClassArray
(
"type"
);
Scope
scope
=
annotation
.
getEnum
(
"scope"
,
JsonComponent
.
Scope
.
class
);
addJsonBean
(
bean
,
types
,
scope
);
}
private
void
addJsonBean
(
Object
bean
,
Class
<?>[]
types
,
Scope
scope
)
{
if
(
bean
instanceof
JsonSerializer
)
{
addSerializerForTypes
((
JsonSerializer
<?>)
bean
,
annotation
.
handle
(),
annotation
.
handleClasses
());
addJsonSerializerBean
((
JsonSerializer
<?>)
bean
,
scope
,
types
);
}
if
(
bean
instanceof
KeyDeserializer
)
{
addKeyDeserializerForTypes
((
KeyDeserializer
)
bean
,
annotation
.
handleClasses
());
else
if
(
bean
instanceof
JsonDeserializer
)
{
addJsonDeserializerBean
((
JsonDeserializer
<?>)
bean
,
types
);
}
if
(
bean
instanceof
JsonDeserializer
)
{
addDeserializerForTypes
((
JsonDeserializer
<?>)
bean
,
annotation
.
handleClasses
());
else
if
(
bean
instanceof
KeyDeserializer
)
{
addKeyDeserializerBean
((
KeyDeserializer
)
bean
,
types
);
}
for
(
Class
<?>
innerClass
:
bean
.
getClass
().
getDeclaredClasses
())
{
if
(!
Modifier
.
isAbstract
(
innerClass
.
getModifiers
())
&&
(
JsonSerializer
.
class
.
isAssignableFrom
(
innerClass
)
||
JsonDeserializer
.
class
.
isAssignableFrom
(
innerClass
)
||
KeyDeserializer
.
class
.
isAssignableFrom
(
innerClass
)))
{
try
{
addJsonBean
(
innerClass
.
newInstance
(),
annotation
);
}
catch
(
Exception
ex
)
{
throw
new
IllegalStateException
(
ex
);
}
if
(
isSuitableInnerClass
(
innerClass
))
{
Object
innerInstance
=
BeanUtils
.
instantiateClass
(
innerClass
);
addJsonBean
(
innerInstance
,
types
,
scope
);
}
}
}
@SuppressWarnings
({
"unchecked"
})
private
<
T
>
void
addSerializerForTypes
(
JsonSerializer
<
T
>
serializer
,
JsonComponent
.
Handle
handle
,
Class
<?>[]
types
)
{
for
(
Class
<?>
type
:
types
)
{
addSerializerWithType
(
serializer
,
handle
,
(
Class
<
T
>)
type
);
}
if
(
types
.
length
==
0
)
{
ResolvableType
type
=
ResolvableType
.
forClass
(
JsonSerializer
.
class
,
serializer
.
getClass
());
addSerializerWithType
(
serializer
,
handle
,
(
Class
<
T
>)
type
.
resolveGeneric
());
}
private
boolean
isSuitableInnerClass
(
Class
<?>
innerClass
)
{
return
!
Modifier
.
isAbstract
(
innerClass
.
getModifiers
())
&&
(
JsonSerializer
.
class
.
isAssignableFrom
(
innerClass
)
||
JsonDeserializer
.
class
.
isAssignableFrom
(
innerClass
)
||
KeyDeserializer
.
class
.
isAssignableFrom
(
innerClass
));
}
private
<
T
>
void
addSerializerWithType
(
JsonSerializer
<
T
>
serializer
,
JsonComponent
.
Handle
handle
,
Class
<?
extends
T
>
type
)
{
if
(
JsonComponent
.
Handle
.
KEYS
.
equals
(
handle
)
)
{
addKeySerializer
(
type
,
serializer
);
}
else
{
addSerializer
(
type
,
s
erializer
);
}
@SuppressWarnings
(
"unchecked"
)
private
<
T
>
void
addJsonSerializerBean
(
JsonSerializer
<
T
>
serializer
,
JsonComponent
.
Scope
scope
,
Class
<?>[]
types
)
{
Class
<
T
>
baseType
=
(
Class
<
T
>)
ResolvableType
.
forClass
(
JsonSerializer
.
class
,
serializer
.
getClass
()).
resolveGeneric
();
addBeanToModule
(
serializer
,
baseType
,
types
,
(
scope
==
Scope
.
VALUES
)
?
this
::
addSerializer
:
this
::
addKeyS
erializer
);
}
@SuppressWarnings
(
{
"unchecked"
}
)
private
<
T
>
void
add
DeserializerForTypes
(
JsonDeserializer
<
T
>
deserializer
,
@SuppressWarnings
(
"unchecked"
)
private
<
T
>
void
add
JsonDeserializerBean
(
JsonDeserializer
<
T
>
deserializer
,
Class
<?>[]
types
)
{
for
(
Class
<?>
type
:
types
)
{
addDeserializer
((
Class
<
T
>)
type
,
deserializer
);
}
if
(
types
.
length
==
0
)
{
addDeserializerWithDeducedType
(
deserializer
);
}
Class
<
T
>
baseType
=
(
Class
<
T
>)
ResolvableType
.
forClass
(
JsonDeserializer
.
class
,
deserializer
.
getClass
())
.
resolveGeneric
();
addBeanToModule
(
deserializer
,
baseType
,
types
,
this
::
addDeserializer
);
}
@SuppressWarnings
({
"unchecked"
})
private
<
T
>
void
addDeserializerWithDeducedType
(
JsonDeserializer
<
T
>
deserializer
)
{
ResolvableType
type
=
ResolvableType
.
forClass
(
JsonDeserializer
.
class
,
deserializer
.
getClass
());
addDeserializer
((
Class
<
T
>)
type
.
resolveGeneric
(),
deserializer
);
private
void
addKeyDeserializerBean
(
KeyDeserializer
deserializer
,
Class
<?>[]
types
)
{
Assert
.
notEmpty
(
types
,
"Type must be specified for KeyDeserializer"
);
addBeanToModule
(
deserializer
,
Object
.
class
,
types
,
this
::
addKeyDeserializer
);
}
private
void
addKeyDeserializerForTypes
(
KeyDeserializer
deserializer
,
Class
<?>[]
types
)
{
@SuppressWarnings
(
"unchecked"
)
private
<
E
,
T
>
void
addBeanToModule
(
E
element
,
Class
<
T
>
baseType
,
Class
<?>[]
types
,
BiConsumer
<
Class
<
T
>,
E
>
consumer
)
{
if
(
ObjectUtils
.
isEmpty
(
types
))
{
consumer
.
accept
(
baseType
,
element
);
return
;
}
for
(
Class
<?>
type
:
types
)
{
addKeyDeserializer
(
type
,
deserializer
);
Assert
.
isAssignable
(
baseType
,
type
);
consumer
.
accept
((
Class
<
T
>)
type
,
element
);
}
}
...
...
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/jackson/JsonComponentModuleTests.java
View file @
bf633fc5
/*
* Copyright 2012-201
8
the original author or authors.
* Copyright 2012-201
9
the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
...
...
@@ -198,12 +198,12 @@ public class JsonComponentModuleTests {
}
@JsonComponent
(
handle
=
JsonComponent
.
Handl
e
.
KEYS
)
@JsonComponent
(
scope
=
JsonComponent
.
Scop
e
.
KEYS
)
static
class
OnlyKeySerializer
extends
NameAndAgeJsonKeyComponent
.
Serializer
{
}
@JsonComponent
(
handle
=
JsonComponent
.
Handle
.
KEYS
,
handleClasses
=
NameAndAge
.
class
)
@JsonComponent
(
scope
=
JsonComponent
.
Scope
.
KEYS
,
type
=
NameAndAge
.
class
)
static
class
OnlyKeyDeserializer
extends
NameAndAgeJsonKeyComponent
.
Deserializer
{
}
...
...
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/jackson/NameAndAge.java
View file @
bf633fc5
/*
* Copyright 2012-201
7
the original author or authors.
* Copyright 2012-201
9
the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
...
...
@@ -22,6 +22,7 @@ import org.springframework.util.ObjectUtils;
* Sample object used for tests.
*
* @author Phillip Webb
* @author Paul Aly
*/
public
final
class
NameAndAge
extends
Name
{
...
...
@@ -37,7 +38,7 @@ public final class NameAndAge extends Name {
}
public
String
asKey
()
{
return
name
+
" is "
+
age
;
return
this
.
name
+
" is "
+
this
.
age
;
}
@Override
...
...
@@ -48,7 +49,6 @@ public final class NameAndAge extends Name {
if
(
obj
==
null
)
{
return
false
;
}
if
(
obj
instanceof
NameAndAge
)
{
NameAndAge
other
=
(
NameAndAge
)
obj
;
boolean
rtn
=
true
;
...
...
@@ -56,7 +56,6 @@ public final class NameAndAge extends Name {
rtn
=
rtn
&&
ObjectUtils
.
nullSafeEquals
(
this
.
age
,
other
.
age
);
return
rtn
;
}
return
super
.
equals
(
obj
);
}
...
...
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/jackson/NameAndAgeJsonKeyComponent.java
View file @
bf633fc5
/*
* Copyright 2012-201
7
the original author or authors.
* Copyright 2012-201
9
the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
...
...
@@ -29,7 +29,7 @@ import com.fasterxml.jackson.databind.SerializerProvider;
*
* @author Paul Aly
*/
@JsonComponent
(
handle
=
JsonComponent
.
Handle
.
KEYS
,
handleClasses
=
NameAndAge
.
class
)
@JsonComponent
(
type
=
NameAndAge
.
class
,
scope
=
JsonComponent
.
Scope
.
KEYS
)
public
class
NameAndAgeJsonKeyComponent
{
public
static
class
Serializer
extends
JsonSerializer
<
NameAndAge
>
{
...
...
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/jackson/NameAndCareerJsonComponent.java
View file @
bf633fc5
/*
* Copyright 2012-201
7
the original author or authors.
* Copyright 2012-201
9
the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
...
...
@@ -30,7 +30,7 @@ import com.fasterxml.jackson.databind.SerializerProvider;
*
* @author Paul Aly
*/
@JsonComponent
(
handleClasses
=
NameAndCareer
.
class
)
@JsonComponent
(
type
=
NameAndCareer
.
class
)
public
class
NameAndCareerJsonComponent
{
public
static
class
Serializer
extends
JsonObjectSerializer
<
Name
>
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment