1.问题描述:
produces = "application/json;charset=GBK"不生效,查询了资料了发现没有文章对此说明,追踪源码发现对应的问题,所以记录起来避免其他人也采坑
2.主流工具类的实现,例如fastjson、jsackjson、GsonJson
3.工具类剖析,重要!!!
关键信息:http重要接口类HttpMessageConverter
JackJson:
关键重要类:AbstractJackson2HttpMessageConverter
AbstractJackson2HttpMessageConverter-->AbstractGenericHttpMessageConverter--> AbstractHttpMessageConverter-->HttpMessageConverter
protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
MediaType contentType = outputMessage.getHeaders().getContentType();
JsonEncoding encoding = getJsonEncoding(contentType);
JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
try {
writePrefix(generator, object);
Object value = object;
Class<?> serializationView = null;
FilterProvider filters = null;
JavaType javaType = null;
if (object instanceof MappingJacksonValue) {
MappingJacksonValue container = (MappingJacksonValue) object;
value = container.getValue();
serializationView = container.getSerializationView();
filters = container.getFilters();
}
if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
javaType = getJavaType(type, null);
}
ObjectWriter objectWriter = (serializationView != null ?
this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer());
if (filters != null) {
objectWriter = objectWriter.with(filters);
}
if (javaType != null && javaType.isContainerType()) {
objectWriter = objectWriter.forType(javaType);
}
SerializationConfig config = objectWriter.getConfig();
if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
objectWriter = objectWriter.with(this.ssePrettyPrinter);
}
objectWriter.writeValue(generator, value);
writeSuffix(generator, object);
generator.flush();
}
catch (InvalidDefinitionException ex) {
throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
}
catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);
}
}
关键点:JsonEncoding
protected JsonEncoding getJsonEncoding(@Nullable MediaType contentType) {
if (contentType != null && contentType.getCharset() != null) {
Charset charset = contentType.getCharset();
for (JsonEncoding encoding : JsonEncoding.values()) {
if (charset.name().equals(encoding.getJavaName())) {
return encoding;
}
}
}
return JsonEncoding.UTF8;
}
关键点:没有对GBK编码的支持
public enum JsonEncoding {
UTF8("UTF-8", false, 8), // N/A for big-endian, really
UTF16_BE("UTF-16BE", true, 16),
UTF16_LE("UTF-16LE", false, 16),
UTF32_BE("UTF-32BE", true, 32),
UTF32_LE("UTF-32LE", false, 32)
;
private final String _javaName;
private final boolean _bigEndian;
private final int _bits;
JsonEncoding(String javaName, boolean bigEndian, int bits)
{
_javaName = javaName;
_bigEndian = bigEndian;
_bits = bits;
}
/**
* Method for accessing encoding name that JDK will support.
*
* @return Matching encoding name that JDK will support.
*/
public String getJavaName() { return _javaName; }
/**
* Whether encoding is big-endian (if encoding supports such
* notion). If no such distinction is made (as is the case for
* {@link #UTF8}), return value is undefined.
*
* @return True for big-endian encodings; false for little-endian
* (or if not applicable)
*/
public boolean isBigEndian() { return _bigEndian; }
public int bits() { return _bits; }
}
FastJson
关键重要类:FastJsonHttpMessageConverter
FastJsonHttpMessageConverter--> AbstractHttpMessageConverter-->HttpMessageConverter
关键点: fastJsonConfig.getCharset(),取的是默认的字符编码,这个初始化可以设置
protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
ByteArrayOutputStream outnew = new ByteArrayOutputStream();
try {
HttpHeaders headers = outputMessage.getHeaders();
SerializeFilter[] globalFilters = fastJsonConfig.getSerializeFilters();
List<SerializeFilter> allFilters = new ArrayList<SerializeFilter>(Arrays.asList(globalFilters));
boolean isJsonp = false;
Object value = strangeCodeForJackson(object);
if (value instanceof FastJsonContainer) {
FastJsonContainer fastJsonContainer = (FastJsonContainer) value;
PropertyPreFilters filters = fastJsonContainer.getFilters();
allFilters.addAll(filters.getFilters());
value = fastJsonContainer.getValue();
}
if (value instanceof MappingFastJsonValue) {
if (!StringUtils.isEmpty(((MappingFastJsonValue) value).getJsonpFunction())) {
isJsonp = true;
}
} else if (value instanceof JSONPObject) {
isJsonp = true;
}
int len = JSON.writeJSONString(outnew, //
fastJsonConfig.getCharset(), //
value, //
fastJsonConfig.getSerializeConfig(), //
//fastJsonConfig.getSerializeFilters(), //
allFilters.toArray(new SerializeFilter[allFilters.size()]),
fastJsonConfig.getDateFormat(), //
JSON.DEFAULT_GENERATE_FEATURE, //
fastJsonConfig.getSerializerFeatures());
if (isJsonp) {
headers.setContentType(APPLICATION_JAVASCRIPT);
}
if (fastJsonConfig.isWriteContentLength()) {
headers.setContentLength(len);
}
outnew.writeTo(outputMessage.getBody());
} catch (JSONException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
} finally {
outnew.close();
}
}
GSON
springboot对Gson的支持也比较简单
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</exclusion>
</exclusions>
</dependency>
关键重要类:AbstractJsonHttpMessageConverter
AbstractJsonHttpMessageConverter-->AbstractGenericHttpMessageConverter--> AbstractHttpMessageConverter-->HttpMessageConverter
关键点:AbstractJsonHttpMessageConverter-->getWriter
protected final void writeInternal(Object o, @Nullable Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
Writer writer = getWriter(outputMessage);
if (this.jsonPrefix != null) {
writer.append(this.jsonPrefix);
}
try {
writeInternal(o, type, writer);
}
catch (Exception ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
writer.flush();
}
private static Writer getWriter(HttpOutputMessage outputMessage) throws IOException {
return new OutputStreamWriter(outputMessage.getBody(), getCharset(outputMessage.getHeaders()));
}
4.总结:
可以明显发现jackjson是没有对GBK编码的支持,fastjson初始化设置字符集编码外(但是设置了默认编码后,整个项目就是用了这个字符集编码(示例如下@Bean),如果项目有需要对GBK的支持建议使用respone方法去拓展或者引用gson)
@Bean
public HttpMessageConverters fastJsonConfigure(){
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
//日期格式化
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
fastJsonConfig.setCharset(Charset.forName("GBK"));
converter.setFastJsonConfig(fastJsonConfig);
return new HttpMessageConverters(converter);
}
采用respone方法也是最简单的方法
response.setContentType("application/json;charset=GBK");
response.getWriter().write(JSONObject.toJSONString(object));