fastjson-对象转json字符串时使用@JSONType注解指定对象字段的顺序
一、简单例子
依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.72</version>
</dependency>
源码:
package com.junjie.test;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
public class Test {
public static void main(String[] args) {
User user = User.builder().userName("01junjie").age("12").atext("程序员").build();
// JSONObject jsonObject = new JSONObject(true);
// jsonObject.put("User",user);
// String s = jsonObject.toJSONString();
String s = JSONObject.toJSONString(user);
System.out.println(s);
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
// @JSONType(orders = {"userName","age","atext"},ignores = {"age"})
static class User{
String userName;
String age;
String atext;
}
}
输出结果:
{“age”:“12”,“atext”:“程序员”,“userName”:“01junjie”}
从结果上看,我们的程序正常输出,但是字段却出现了乱序,某些时候,我们需要指定顺序,我们可以将上述注释的注解@JSONType放开。
package com.junjie.test;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
public class Test {
public static void main(String[] args) {
User user = User.builder().userName("01junjie").age("12").atext("程序员").build();
// JSONObject jsonObject = new JSONObject(true);
// jsonObject.put("User",user);
// String s = jsonObject.toJSONString();
String s = JSONObject.toJSONString(user);
System.out.println(s);
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@JSONType(orders = {"userName","age","atext"},ignores = {"age"})
static class User{
String userName;
String age;
String atext;
}
}
输出结果:
{“userName”:“01junjie”,“atext”:“程序员”}
@JSONType(orders = {"userName","age","atext"},ignores = {"age"})
这行代码的作用是,指定了字段的固定顺序,以及要排除的字段。
二、@JSONType简单介绍
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface JSONType {
boolean asm() default true;
String[] orders() default {};
/**
* @since 1.2.6
*/
String[] includes() default {};
String[] ignores() default {};
、、、、
、、、、
}
这是该注解的部分源码,这几个属性已经基本够用了,简单介绍一下作用:
orders:生成json时,指定字段按顺序排序
includes:生成json时,指定包含的字段
ignores:生成json时,指定忽略的字段
有时若觉得使用这个注解比较繁琐,也可以配合PropertyPreFilters 一起使用,可参考:
https://blog.csdn.net/weixin_41979002/article/details/122100260
三、@JSONType源码解析
若上述已经解决你的问题了,赶时间的,就可以不用往下看了,下面进入正题。
3.1 看源码的原因
由于业务需求,需要对java对象转json后,指定字段的固定顺序。
提前说明一下,本人在今天之前,是不曾知道fastjson这个依赖有这个注解的,更不知道该注解的存在及作用,所以起初一直百度都没有找到有@JSONType相关的解决方案(显然,就是我的描述问题,没描述到点子上),这也就映射出一个问题,许多工具也许帮你实现了很多功能,但你不知道也不懂使用,以至于百度也由于你的问题描述偏差,导致总与答案失之交臂。
这个时候该这么办,答案就是看源码。
3.2 分析源码的流程
3.2.1 本人的代码
public static void main(String[] args) {
User user = User.builder().userName("01junjie").age("12").atext("程序员").build();
// JSONObject jsonObject = new JSONObject();
// jsonObject.put("User",user);
// String s = jsonObject.toJSONString();
String s = JSONObject.toJSONString(user);
System.out.println(s);
}
3.2.2 toJSONString就是我们的入口
我们追溯到底后可以看到,最终都会调用下面这个接口的方法:
public interface ObjectSerializer {
void write(JSONSerializer serializer, //
Object object, //
Object fieldName, //
Type fieldType, //
int features) throws IOException;
}
而这个接口有许多实现类,因此我们无法知道我们的程序运行了哪个实现类的实现方法,于是我们回退上一步调用该方法的的入口:
public final void write(Object object) {
if (object == null) {
out.writeNull();
return;
}
Class<?> clazz = object.getClass();
ObjectSerializer writer = getObjectWriter(clazz);
try {
writer.write(this, object, null, null, 0);
} catch (IOException e) {
throw new JSONException(e.getMessage(), e);
}
}
ObjectSerializer writer = getObjectWriter(clazz);这行代码,便是获取一个Serializer,我们可以打上断点,看一下获取到的是哪个实现类,如下:
此种分析源码的方式很常用,从这里可以看到,该行代码可以根据我们的java对象创建出一个专属的Serializer。(如何创建的,不需要追究,不重要)
到了这里,我们已经能确定了,将我们的user对象转换为json字符串的主要实现类是JavaBeanSerializer
3.2.3 进入JavaBeanSerializer
在分析JavaBeanSerializer之前,我们先要明确自己的需求和目标:
1、为啥user的字段会出现乱序?
我们要找到方法中将这些字段转被换成字符串的代码位置,再一层层往上找。
先看该类实现的write:
protected void write(JSONSerializer serializer, //
Object object, //
Object fieldName, //
Type fieldType, //
int features,
boolean unwrapped
) throws IOException {
//省略部分代码
final FieldSerializer[] getters;
if (out.sortField) {
getters = this.sortedGetters;
} else {
getters = this.getters;
}
SerialContext parent = serializer.context;
if (!this.beanInfo.beanType.isEnum()) {
serializer.setContext(parent, object, fieldName, this.beanInfo.features, features);
}
final boolean writeAsArray = isWriteAsArray(serializer, features);
FieldSerializer errorFieldSerializer = null;
try {
final char startSeperator = writeAsArray ? '[' : '{';
final char endSeperator = writeAsArray ? ']' : '}';
if (!unwrapped) {
out.append(startSeperator);
}
//省略部分代码
getters 便是我们的目标,打上断点,再运行
可以看到sortedGetters里面的直就已经出现了乱序(重新做了排序),排在第一位的就是age。
我们想要的是
static class User{
String userName;
String age;
String atext;
}
userName、age、atext这样的顺序才对。
于是我们可以搜索一下sortedGetters是在哪个位置做的初始化,如下:
public JavaBeanSerializer(SerializeBeanInfo beanInfo) {
this.beanInfo = beanInfo;
sortedGetters = new FieldSerializer[beanInfo.sortedFields.length];
for (int i = 0; i < sortedGetters.length; ++i) {
sortedGetters[i] = new FieldSerializer(beanInfo.beanType, beanInfo.sortedFields[i]);
}
//省略部分代码
sortedGetters 来自于beanInfo
进入SerializeBeanInfo:
public class SerializeBeanInfo {
protected final Class<?> beanType;
protected final String typeName;
protected final String typeKey;
protected final JSONType jsonType;
protected final FieldInfo[] fields;
protected final FieldInfo[] sortedFields;
protected int features;
public SerializeBeanInfo(Class<?> beanType, //
JSONType jsonType, //
String typeName, //
String typeKey,
int features,
FieldInfo[] fields, //
FieldInfo[] sortedFields
){
this.beanType = beanType;
this.jsonType = jsonType;
this.typeName = typeName;
this.typeKey = typeKey;
this.features = features;
this.fields = fields;
this.sortedFields = sortedFields;
}
}
再看看哪里调用了这个类的构造方法,如下:
public static SerializeBeanInfo buildBeanInfo(Class<?> beanType //
, Map<String,String> aliasMap //
, PropertyNamingStrategy propertyNamingStrategy //
, boolean fieldBased //
){
JSONType jsonType = TypeUtils.getAnnotation(beanType,JSONType.class);
String[] orders = null;
final int features;
String typeName = null, typeKey = null;
if(jsonType != null){
orders = jsonType.orders();
typeName = jsonType.typeName();
if(typeName.length() == 0){
typeName = null;
}
PropertyNamingStrategy jsonTypeNaming = jsonType.naming();
if (jsonTypeNaming != PropertyNamingStrategy.CamelCase) {
propertyNamingStrategy = jsonTypeNaming;
}
features = SerializerFeature.of(jsonType.serialzeFeatures());
for(Class<?> supperClass = beanType.getSuperclass()
; supperClass != null && supperClass != Object.class
; supperClass = supperClass.getSuperclass()){
JSONType superJsonType = TypeUtils.getAnnotation(supperClass,JSONType.class);
if(superJsonType == null){
break;
}
typeKey = superJsonType.typeKey();
if(typeKey.length() != 0){
break;
}
}
for(Class<?> interfaceClass : beanType.getInterfaces()){
JSONType superJsonType = TypeUtils.getAnnotation(interfaceClass,JSONType.class);
if(superJsonType != null){
typeKey = superJsonType.typeKey();
if(typeKey.length() != 0){
break;
}
}
}
if(typeKey != null && typeKey.length() == 0){
typeKey = null;
}
} else{
features = 0;
}
// fieldName,field ,先生成fieldName的快照,减少之后的findField的轮询
Map<String,Field> fieldCacheMap = new HashMap<String,Field>();
ParserConfig.parserAllFieldToCache(beanType, fieldCacheMap);
List<FieldInfo> fieldInfoList = fieldBased
? computeGettersWithFieldBase(beanType, aliasMap, false, propertyNamingStrategy) //
: computeGetters(beanType, jsonType, aliasMap, fieldCacheMap, false, propertyNamingStrategy);
FieldInfo[] fields = new FieldInfo[fieldInfoList.size()];
fieldInfoList.toArray(fields);
FieldInfo[] sortedFields;
List<FieldInfo> sortedFieldList;
if(orders != null && orders.length != 0){
sortedFieldList = fieldBased
? computeGettersWithFieldBase(beanType, aliasMap, true, propertyNamingStrategy) //
: computeGetters(beanType, jsonType, aliasMap, fieldCacheMap, true, propertyNamingStrategy);
} else{
sortedFieldList = new ArrayList<FieldInfo>(fieldInfoList);
Collections.sort(sortedFieldList);
}
sortedFields = new FieldInfo[sortedFieldList.size()];
sortedFieldList.toArray(sortedFields);
if(Arrays.equals(sortedFields, fields)){
sortedFields = fields;
}
return new SerializeBeanInfo(beanType, jsonType, typeName, typeKey, features, fields, sortedFields);
}
这个方法便是我们要找到的最重要的方法,JSONType jsonType = TypeUtils.getAnnotation(beanType,JSONType.class);这行代码映入我们的眼帘,一切问题都迎刃而解了。大概意思就是,获取class类上的@JSONType 注解信息,若是存在,则根据指定的顺序排序。
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface JSONType {
boolean asm() default true;
String[] orders() default {};
/**
* @since 1.2.6
*/
String[] includes() default {};
String[] ignores() default {};
SerializerFeature[] serialzeFeatures() default {};
Feature[] parseFeatures() default {};
boolean alphabetic() default true;
Class<?> mappingTo() default Void.class;
Class<?> builder() default Void.class;
/**
* @since 1.2.11
*/
String typeName() default "";
/**
* @since 1.2.32
*/
String typeKey() default "";
/**
* @since 1.2.11
*/
Class<?>[] seeAlso() default{};
/**
* @since 1.2.14
*/
Class<?> serializer() default Void.class;
/**
* @since 1.2.14
*/
Class<?> deserializer() default Void.class;
boolean serializeEnumAsJavaBean() default false;
PropertyNamingStrategy naming() default PropertyNamingStrategy.CamelCase;
/**
* @since 1.2.49
*/
Class<? extends SerializeFilter>[] serialzeFilters() default {};
/**
* @since 1.2.71
* @return
*/
Class<? extends ParserConfig.AutoTypeCheckHandler> autoTypeCheckHandler() default ParserConfig.AutoTypeCheckHandler.class;
}
更多推荐
所有评论(0)