Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
爱吃血肠
spring-framework
提交
903770f0
S
spring-framework
项目概览
爱吃血肠
/
spring-framework
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
S
spring-framework
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
903770f0
编写于
7月 21, 2016
作者:
S
Sebastien Deleuze
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add JsonView and type resolution support to JacksonJsonEncoder
Issue: SPR-14158
上级
4d035e3a
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
235 addition
and
14 deletion
+235
-14
spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageWriterResultHandlerTests.java
...lt/method/annotation/MessageWriterResultHandlerTests.java
+0
-2
spring-web/src/main/java/org/springframework/http/codec/json/AbstractJacksonJsonCodec.java
...ngframework/http/codec/json/AbstractJacksonJsonCodec.java
+127
-0
spring-web/src/main/java/org/springframework/http/codec/json/JacksonJsonEncoder.java
...g/springframework/http/codec/json/JacksonJsonEncoder.java
+42
-12
spring-web/src/test/java/org/springframework/http/codec/json/JacksonJsonEncoderTests.java
...ingframework/http/codec/json/JacksonJsonEncoderTests.java
+66
-0
未找到文件。
spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageWriterResultHandlerTests.java
浏览文件 @
903770f0
...
...
@@ -148,7 +148,6 @@ public class MessageWriterResultHandlerTests {
}
@Test
// SPR-13318
@Ignore
public
void
jacksonTypeWithSubType
()
throws
Exception
{
SimpleBean
body
=
new
SimpleBean
(
123L
,
"foo"
);
ResolvableType
type
=
ResolvableType
.
forClass
(
Identifiable
.
class
);
...
...
@@ -159,7 +158,6 @@ public class MessageWriterResultHandlerTests {
}
@Test
// SPR-13318
@Ignore
public
void
jacksonTypeWithSubTypeOfListElement
()
throws
Exception
{
List
<
SimpleBean
>
body
=
Arrays
.
asList
(
new
SimpleBean
(
123L
,
"foo"
),
new
SimpleBean
(
456L
,
"bar"
));
ResolvableType
type
=
ResolvableType
.
forClassWithGenerics
(
List
.
class
,
Identifiable
.
class
);
...
...
spring-web/src/main/java/org/springframework/http/codec/json/AbstractJacksonJsonCodec.java
0 → 100644
浏览文件 @
903770f0
/*
* Copyright 2002-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org.springframework.http.codec.json
;
import
java.lang.reflect.ParameterizedType
;
import
java.lang.reflect.Type
;
import
java.lang.reflect.TypeVariable
;
import
java.nio.charset.StandardCharsets
;
import
java.util.Arrays
;
import
java.util.List
;
import
com.fasterxml.jackson.databind.JavaType
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.databind.type.TypeFactory
;
import
org.springframework.core.ResolvableType
;
import
org.springframework.util.MimeType
;
/**
* @author Sebastien Deleuze
*/
public
class
AbstractJacksonJsonCodec
{
protected
static
final
List
<
MimeType
>
JSON_MIME_TYPES
=
Arrays
.
asList
(
new
MimeType
(
"application"
,
"json"
,
StandardCharsets
.
UTF_8
),
new
MimeType
(
"application"
,
"*+json"
,
StandardCharsets
.
UTF_8
));
protected
final
ObjectMapper
mapper
;
protected
AbstractJacksonJsonCodec
(
ObjectMapper
mapper
)
{
this
.
mapper
=
mapper
;
}
/**
* Return the Jackson {@link JavaType} for the specified type and context class.
* <p>The default implementation returns {@code typeFactory.constructType(type, contextClass)},
* but this can be overridden in subclasses, to allow for custom generic collection handling.
* For instance:
* <pre class="code">
* protected JavaType getJavaType(Type type) {
* if (type instanceof Class && List.class.isAssignableFrom((Class)type)) {
* return TypeFactory.collectionType(ArrayList.class, MyBean.class);
* } else {
* return super.getJavaType(type);
* }
* }
* </pre>
* @param type the generic type to return the Jackson JavaType for
* @param contextClass a context class for the target type, for example a class
* in which the target type appears in a method signature (can be {@code null})
* @return the Jackson JavaType
*/
protected
JavaType
getJavaType
(
Type
type
,
Class
<?>
contextClass
)
{
TypeFactory
typeFactory
=
this
.
mapper
.
getTypeFactory
();
if
(
contextClass
!=
null
)
{
ResolvableType
resolvedType
=
ResolvableType
.
forType
(
type
);
if
(
type
instanceof
TypeVariable
)
{
ResolvableType
resolvedTypeVariable
=
resolveVariable
(
(
TypeVariable
<?>)
type
,
ResolvableType
.
forClass
(
contextClass
));
if
(
resolvedTypeVariable
!=
ResolvableType
.
NONE
)
{
return
typeFactory
.
constructType
(
resolvedTypeVariable
.
resolve
());
}
}
else
if
(
type
instanceof
ParameterizedType
&&
resolvedType
.
hasUnresolvableGenerics
())
{
ParameterizedType
parameterizedType
=
(
ParameterizedType
)
type
;
Class
<?>[]
generics
=
new
Class
<?>[
parameterizedType
.
getActualTypeArguments
().
length
];
Type
[]
typeArguments
=
parameterizedType
.
getActualTypeArguments
();
for
(
int
i
=
0
;
i
<
typeArguments
.
length
;
i
++)
{
Type
typeArgument
=
typeArguments
[
i
];
if
(
typeArgument
instanceof
TypeVariable
)
{
ResolvableType
resolvedTypeArgument
=
resolveVariable
(
(
TypeVariable
<?>)
typeArgument
,
ResolvableType
.
forClass
(
contextClass
));
if
(
resolvedTypeArgument
!=
ResolvableType
.
NONE
)
{
generics
[
i
]
=
resolvedTypeArgument
.
resolve
();
}
else
{
generics
[
i
]
=
ResolvableType
.
forType
(
typeArgument
).
resolve
();
}
}
else
{
generics
[
i
]
=
ResolvableType
.
forType
(
typeArgument
).
resolve
();
}
}
return
typeFactory
.
constructType
(
ResolvableType
.
forClassWithGenerics
(
resolvedType
.
getRawClass
(),
generics
).
getType
());
}
}
return
typeFactory
.
constructType
(
type
);
}
private
ResolvableType
resolveVariable
(
TypeVariable
<?>
typeVariable
,
ResolvableType
contextType
)
{
ResolvableType
resolvedType
;
if
(
contextType
.
hasGenerics
())
{
resolvedType
=
ResolvableType
.
forType
(
typeVariable
,
contextType
);
if
(
resolvedType
.
resolve
()
!=
null
)
{
return
resolvedType
;
}
}
resolvedType
=
resolveVariable
(
typeVariable
,
contextType
.
getSuperType
());
if
(
resolvedType
.
resolve
()
!=
null
)
{
return
resolvedType
;
}
for
(
ResolvableType
ifc
:
contextType
.
getInterfaces
())
{
resolvedType
=
resolveVariable
(
typeVariable
,
ifc
);
if
(
resolvedType
.
resolve
()
!=
null
)
{
return
resolvedType
;
}
}
return
ResolvableType
.
NONE
;
}
}
spring-web/src/main/java/org/springframework/http/codec/json/JacksonJsonEncoder.java
浏览文件 @
903770f0
...
...
@@ -19,8 +19,9 @@ package org.springframework.http.codec.json;
import
java.io.IOException
;
import
java.io.OutputStream
;
import
java.nio.ByteBuffer
;
import
java.
nio.charset.StandardCharsets
;
import
java.
util.List
;
import
com.fasterxml.jackson.annotation.JsonView
;
import
com.fasterxml.jackson.databind.JavaType
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.databind.ObjectWriter
;
...
...
@@ -29,11 +30,13 @@ import org.reactivestreams.Publisher;
import
reactor.core.publisher.Flux
;
import
reactor.core.publisher.Mono
;
import
org.springframework.core.MethodParameter
;
import
org.springframework.core.ResolvableType
;
import
org.springframework.core.codec.CodecException
;
import
org.springframework.core.codec.
Abstract
Encoder
;
import
org.springframework.core.codec.Encoder
;
import
org.springframework.core.io.buffer.DataBuffer
;
import
org.springframework.core.io.buffer.DataBufferFactory
;
import
org.springframework.http.converter.json.Jackson2ObjectMapperBuilder
;
import
org.springframework.util.Assert
;
import
org.springframework.util.MimeType
;
...
...
@@ -45,7 +48,7 @@ import org.springframework.util.MimeType;
* @since 5.0
* @see JacksonJsonDecoder
*/
public
class
JacksonJsonEncoder
extends
AbstractEncoder
<
Object
>
{
public
class
JacksonJsonEncoder
extends
Abstract
JacksonJsonCodec
implements
Encoder
<
Object
>
{
private
static
final
ByteBuffer
START_ARRAY_BUFFER
=
ByteBuffer
.
wrap
(
new
byte
[]{
'['
});
...
...
@@ -54,21 +57,26 @@ public class JacksonJsonEncoder extends AbstractEncoder<Object> {
private
static
final
ByteBuffer
END_ARRAY_BUFFER
=
ByteBuffer
.
wrap
(
new
byte
[]{
']'
});
private
final
ObjectMapper
mapper
;
public
JacksonJsonEncoder
()
{
this
(
new
ObjectMapper
());
super
(
Jackson2ObjectMapperBuilder
.
json
().
build
());
}
public
JacksonJsonEncoder
(
ObjectMapper
mapper
)
{
super
(
new
MimeType
(
"application"
,
"json"
,
StandardCharsets
.
UTF_8
),
new
MimeType
(
"application"
,
"*+json"
,
StandardCharsets
.
UTF_8
));
Assert
.
notNull
(
mapper
,
"'mapper' must not be null"
);
super
(
mapper
);
}
this
.
mapper
=
mapper
;
@Override
public
boolean
canEncode
(
ResolvableType
elementType
,
MimeType
mimeType
,
Object
...
hints
)
{
if
(
mimeType
==
null
)
{
return
true
;
}
return
JSON_MIME_TYPES
.
stream
().
anyMatch
(
m
->
m
.
isCompatibleWith
(
mimeType
));
}
@Override
public
List
<
MimeType
>
getEncodableMimeTypes
()
{
return
JSON_MIME_TYPES
;
}
@Override
public
Flux
<
DataBuffer
>
encode
(
Publisher
<?>
inputStream
,
DataBufferFactory
bufferFactory
,
...
...
@@ -97,7 +105,29 @@ public class JacksonJsonEncoder extends AbstractEncoder<Object> {
private
DataBuffer
encodeValue
(
Object
value
,
DataBufferFactory
bufferFactory
,
ResolvableType
type
)
{
TypeFactory
typeFactory
=
this
.
mapper
.
getTypeFactory
();
JavaType
javaType
=
typeFactory
.
constructType
(
type
.
getType
());
ObjectWriter
writer
=
this
.
mapper
.
writerFor
(
javaType
);
MethodParameter
returnType
=
(
type
.
getSource
()
instanceof
MethodParameter
?
(
MethodParameter
)
type
.
getSource
()
:
null
);
if
(
type
!=
null
&&
value
!=
null
&&
type
.
isAssignableFrom
(
value
.
getClass
()))
{
javaType
=
getJavaType
(
type
.
getType
(),
null
);
}
ObjectWriter
writer
;
if
(
returnType
!=
null
&&
returnType
.
getMethodAnnotation
(
JsonView
.
class
)
!=
null
)
{
JsonView
annotation
=
returnType
.
getMethodAnnotation
(
JsonView
.
class
);
Class
<?>[]
classes
=
annotation
.
value
();
if
(
classes
.
length
!=
1
)
{
throw
new
IllegalArgumentException
(
"@JsonView only supported for response body advice with exactly 1 class argument: "
+
returnType
);
}
writer
=
this
.
mapper
.
writerWithView
(
classes
[
0
]);
}
else
{
writer
=
this
.
mapper
.
writer
();
}
if
(
javaType
!=
null
&&
javaType
.
isContainerType
())
{
writer
=
writer
.
forType
(
javaType
);
}
DataBuffer
buffer
=
bufferFactory
.
allocateBuffer
();
OutputStream
outputStream
=
buffer
.
asOutputStream
();
...
...
spring-web/src/test/java/org/springframework/http/codec/json/JacksonJsonEncoderTests.java
浏览文件 @
903770f0
...
...
@@ -18,9 +18,11 @@ package org.springframework.http.codec.json;
import
com.fasterxml.jackson.annotation.JsonTypeInfo
;
import
com.fasterxml.jackson.annotation.JsonTypeName
;
import
com.fasterxml.jackson.annotation.JsonView
;
import
org.junit.Before
;
import
org.junit.Test
;
import
reactor.core.publisher.Flux
;
import
reactor.core.publisher.Mono
;
import
reactor.test.TestSubscriber
;
import
org.springframework.core.ResolvableType
;
...
...
@@ -92,6 +94,22 @@ public class JacksonJsonEncoderTests extends AbstractDataBufferAllocatingTestCas
stringConsumer
(
"]"
));
}
@Test
public
void
jsonView
()
throws
Exception
{
JacksonViewBean
bean
=
new
JacksonViewBean
();
bean
.
setWithView1
(
"with"
);
bean
.
setWithView2
(
"with"
);
bean
.
setWithoutView
(
"without"
);
ResolvableType
type
=
ResolvableType
.
forMethodReturnType
(
JacksonController
.
class
.
getMethod
(
"foo"
));
Flux
<
DataBuffer
>
output
=
this
.
encoder
.
encode
(
Mono
.
just
(
bean
),
this
.
bufferFactory
,
type
,
null
);
TestSubscriber
.
subscribe
(
output
)
.
assertComplete
()
.
assertNoError
()
.
assertValuesWith
(
stringConsumer
(
"{\"withView1\":\"with\"}"
));
}
@JsonTypeInfo
(
use
=
JsonTypeInfo
.
Id
.
NAME
,
include
=
JsonTypeInfo
.
As
.
PROPERTY
,
property
=
"type"
)
private
static
class
ParentClass
{
...
...
@@ -105,4 +123,52 @@ public class JacksonJsonEncoderTests extends AbstractDataBufferAllocatingTestCas
private
static
class
Bar
extends
ParentClass
{
}
private
interface
MyJacksonView1
{}
private
interface
MyJacksonView2
{}
@SuppressWarnings
(
"unused"
)
private
static
class
JacksonViewBean
{
@JsonView
(
MyJacksonView1
.
class
)
private
String
withView1
;
@JsonView
(
MyJacksonView2
.
class
)
private
String
withView2
;
private
String
withoutView
;
public
String
getWithView1
()
{
return
withView1
;
}
public
void
setWithView1
(
String
withView1
)
{
this
.
withView1
=
withView1
;
}
public
String
getWithView2
()
{
return
withView2
;
}
public
void
setWithView2
(
String
withView2
)
{
this
.
withView2
=
withView2
;
}
public
String
getWithoutView
()
{
return
withoutView
;
}
public
void
setWithoutView
(
String
withoutView
)
{
this
.
withoutView
=
withoutView
;
}
}
private
static
class
JacksonController
{
@JsonView
(
MyJacksonView1
.
class
)
public
JacksonViewBean
foo
()
{
return
null
;
}
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录