最近在进行接口联调时,频繁使用LomBok@Data,BeanUtils以及fastjson,因此来总结学习一下。在进行web业务开发时,会频繁的将Bean与Json字符串进行转换,其中在使用JSONObject.toJSONString(bean)来进行转换时,发现转换成的json中出现字段缺失,后来研究了一下,原来是JSONObject.toJSONString(bean)默认会将bean中值为null的过滤掉,可以使用SerializerFeature属性来控制转换规则。
准备一些测试类
public enum EnumT {
Tom("tom", 13),
Jack("jack", 18);
EnumT(String name, int age) {}
}
@Data
public class Dog {
private Integer size;
private String name;
public Dog(Integer size, String name) {
this.size = size;
this.name = name;
}
}
@Data
public class User {
private Integer age;
private String name;
private boolean leader;
private List<Dog> logList;
private EnumT enumT;
private Date birth;
public User(Integer age, String name, List<Dog> logList,
boolean leader, EnumT enumT, Date birth) {
this.age = age;
this.name = name;
this.logList = logList;
this.leader = leader;
this.enumT = enumT;
this.birth = birth;
}
/**
* You can annotate any field with @Getter and/or @Setter, to let lombok generate the default getter/setter automatically.
* A default getter simply returns the field, and is named getFoo if the field is called foo (or isFoo if the field's type is boolean).
* A default setter is named setFoo if the field is called foo, returns void, and takes 1 parameter of the same type as the field.
* It simply sets the field to this value.
*
*/
}
Bean转换为Json对象
public class JsonTest {
public static void main(String[] args) {
List<Dog> dogList = new ArrayList<>();
for(int i=0; i<3; i++){
dogList.add(new Dog(i, "name"+i));
//dogList.add(new Dog(i, null));
}
User user = new User(18, "", dogList, false, EnumT.Jack, new Date());
System.out.println(user.toString());
//对象转为String,默认情况下会忽略值为Null的key-value对
String s1 = JSONObject.toJSONString(user);
System.out.println(s1);
//是否输出值为null的字段,默认为
//String s2 = JSONObject.toJSONString(user, SerializerFeature.WRITE_MAP_NULL_FEATURES);
输出key时是否使用双引号,默认为true
//String s2 = JSONObject.toJSONString(user, SerializerFeature.QuoteFieldNames);
//使用单引号而不是双引号,默认为false
//String s2 = JSONObject.toJSONString(user, SerializerFeature.UseSingleQuotes);
//Enum输出name()或者original,默认为false
//String s2 = JSONObject.toJSONString(user, SerializerFeature.WriteEnumUsingToString);
//Date使用ISO8601格式输出,默认为false
//String s2 = JSONObject.toJSONString(user, SerializerFeature.UseISO8601DateFormat);
//按字段名称排序后输出。默认为false
String s2 = JSONObject.toJSONString(user, SerializerFeature.SortField);
System.out.println(s2);
//将String转化为对象
//User u = JSONObject.parseObject(s2, User.class);
//System.out.println(u.toString());
}
}
如上所示,可以使用SerializerFeature来控制具体的转换规则
其具体的控制规则如下所示:
QuoteFieldNames 输出key时是否使用双引号,默认为true
UseSingleQuotes 使用单引号而不是双引号,默认为false
WriteMapNullValue 是否输出值为null的字段,默认为false
WriteEnumUsingToString Enum输出name()或者original,默认为false
UseISO8601DateFormat Date使用ISO8601格式输出,默认为false
WriteNullListAsEmpty List字段如果为null,输出为[],而非null
WriteNullStringAsEmpty 字符类型字段如果为null,输出为”“,而非null
WriteNullNumberAsZero 数值字段如果为null,输出为0,而非null
WriteNullBooleanAsFalse Boolean字段如果为null,输出为false,而非null
SkipTransientField 如果是true,类中的Get方法对应的Field是transient,序列化时将会被忽略。默认为true
SortField 按字段名称排序后输出。默认为false
WriteTabAsSpecial 把\t做转义输出,默认为false 不推荐
PrettyFormat 结果是否格式化,默认为false
WriteClassName 序列化时写入类型信息,默认为false。反序列化是需用到
DisableCircularReferenceDetect 消除对同一对象循环引用的问题,默认为false
WriteSlashAsSpecial 对斜杠’/’进行转义
BrowserCompatible 将中文都会序列化为\uXXXX格式,字节数会多一些,但是能兼容IE 6,默认为false
WriteDateUseDateFormat 全局修改日期格式,默认为false。JSON.DEFFAULT_DATE_FORMAT = “yyyy-MM-dd”;JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat);
toJSONString方法源码简析
public static String toJSONString(Object object, //
SerializeConfig config, //
SerializeFilter[] filters, //
String dateFormat, //
int defaultFeatures, //
SerializerFeature... features) {
SerializeWriter out = new SerializeWriter(null, defaultFeatures, features);
try {
JSONSerializer serializer = new JSONSerializer(out, config);
if (dateFormat != null && dateFormat.length() != 0) {
serializer.setDateFormat(dateFormat);
serializer.config(SerializerFeature.WriteDateUseDateFormat, true);
}
if (filters != null) {
for (SerializeFilter filter : filters) {
serializer.addFilter(filter);
}
}
serializer.write(object);
return out.toString();
} finally {
out.close();
}
}
传入待转换的Bean以及各种配置项,通过JSONSerializer的write方法来转换输出json
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);
}
}
由于带转换的bean可能是个复杂类型,因此需要先获取bean的Class对象信息,这样便可以利用反射来分析bean中属性类型等信息。ObjectSerializer为一个核心接口,定义了某种类型的具体转换输出实现
public interface ObjectSerializer {
/**
* fastjson invokes this call-back method during serialization when it encounters a field of the
* specified type.
* @param serializer
* @param object src the object that needs to be converted to Json.
* @param fieldName parent object field name
* @param fieldType parent object field type
* @param features parent object field serializer features
* @throws IOException
*/
void write(JSONSerializer serializer, //
Object object, //
Object fieldName, //
Type fieldType, //
int features) throws IOException;
}
其中一部分默认实现如下:
这里以Boolean类型为例:
如果判断object为boolean类型,则根据value值来输出true或者false
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
SerializeWriter out = serializer.out;
Boolean value = (Boolean) object;
if (value == null) {
out.writeNull(SerializerFeature.WriteNullBooleanAsFalse);
return;
}
if (value.booleanValue()) {
out.write("true");
} else {
out.write("false");
}
}
其中SerializeWriter用来实现最终json字符串的保存,JSONSerializer实现根据Object类型将object转换输出到SerializeWriter中。
public final class SerializeWriter extends Writer {
private final static ThreadLocal<char[]> bufLocal = new ThreadLocal<char[]>();
private final static ThreadLocal<byte[]> bytesBufLocal = new ThreadLocal<byte[]>();
private static int BUFFER_THRESHOLD = 1024 * 128;
static {
try {
String prop = IOUtils.getStringProperty("fastjson.serializer_buffer_threshold");
if (prop != null && prop.length() > 0) {
int serializer_buffer_threshold = Integer.parseInt(prop);
if (serializer_buffer_threshold >= 64 && serializer_buffer_threshold <= 1024 * 64) {
BUFFER_THRESHOLD = serializer_buffer_threshold * 1024;
}
}
} catch (Throwable error) {
// skip
}
}
protected char buf[];
其中buf[]用来存储最终的json字符串。
研究一下如何根据class对象来获取对应的ObjectSerializer
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);
}
}
核心方法getObjectWriter
private ObjectSerializer getObjectWriter(Class<?> clazz, boolean create) {
ObjectSerializer writer = get(clazz);
首先尝试从缓存中获取相应ObjectSerializer,
public final ObjectSerializer get(Type type) {
Type mixin = JSON.getMixInAnnotations(type);
if (null == mixin) {
return this.serializers.get(type);
}
IdentityHashMap<Type, ObjectSerializer> mixInClasses = this.mixInSerializers.get(type);
if (mixInClasses == null) {
return null;
}
return mixInClasses.get(mixin);
}
如果没有,则循环判断该类型为何种类型,每一种类型都相应处理
if (writer == null) {
String className = clazz.getName();
Class<?> superClass;
if (Map.class.isAssignableFrom(clazz)) {
put(clazz, writer = MapSerializer.instance);
} else if (List.class.isAssignableFrom(clazz)) {
put(clazz, writer = ListSerializer.instance);
} else if (Collection.class.isAssignableFrom(clazz)) {
put(clazz, writer = CollectionCodec.instance);
} else if (Date.class.isAssignableFrom(clazz)) {
put(clazz, writer = DateCodec.instance);
} else if (JSONAware.class.isAssignableFrom(clazz)) {
put(clazz, writer = JSONAwareSerializer.instance);
} else if (JSONSerializable.class.isAssignableFrom(clazz)) {
put(clazz, writer = JSONSerializableSerializer.instance);
} else if (JSONStreamAware.class.isAssignableFrom(clazz)) {
put(clazz, writer = MiscCodec.instance);
} else if (clazz.isEnum()) {
整个处理过程很复杂,涉及到底层class对象的处理,总结一下JSONObject.toJSONString
SerializeWriter负责保存转换输出的字符串
JSONSerializer负责根据Bean类型来进行相应处理转换
ObjectSerializer具体类型转换输出的接口定义
Json对象转换为Bean
传入待转换字符串,传入Bean class
User u = JSONObject.parseObject(s2, User.class);
System.out.println(u.toString());
解析字符串方法底层封装方法
之后好好研究一下编译原理,下面是对于特定数据结构进行解析
public static <T> T parseObject(String input, Type clazz, ParserConfig config, ParseProcess processor,
int featureValues, Feature... features) {
if (input == null || input.isEmpty()) {
return null;
}
if (features != null) {
for (Feature feature : features) {
featureValues |= feature.mask;
}
}
DefaultJSONParser parser = new DefaultJSONParser(input, config, featureValues);
if (processor != null) {
if (processor instanceof ExtraTypeProvider) {
parser.getExtraTypeProviders().add((ExtraTypeProvider) processor);
}
if (processor instanceof ExtraProcessor) {
parser.getExtraProcessors().add((ExtraProcessor) processor);
}
if (processor instanceof FieldTypeResolver) {
parser.setFieldTypeResolver((FieldTypeResolver) processor);
}
}
T value = (T) parser.parseObject(clazz, null);
parser.handleResovleTask(value);
parser.close();
return (T) value;
}
其中Feature可以控制解析规则
构造解析器
DefaultJSONParser parser = new DefaultJSONParser(input, config, featureValues);
构造解析器时,判断字符串最外层是一个json对象{}还是json数组[]
public DefaultJSONParser(final Object input, final JSONLexer lexer, final ParserConfig config){
this.lexer = lexer;
this.input = input;
this.config = config;
this.symbolTable = config.symbolTable;
int ch = lexer.getCurrent();
if (ch == '{') {
lexer.next();
((JSONLexerBase) lexer).token = JSONToken.LBRACE;
} else if (ch == '[') {
lexer.next();
((JSONLexerBase) lexer).token = JSONToken.LBRACKET;
} else {
lexer.nextToken(); // prime the pump
}
}
对于整个解析器进行可选的规则配置之后,进行解析
T value = (T) parser.parseObject(clazz, null);
解析过程,对于json中字符可能是char或者string或者嵌套json,分情况进行处理
public <T> T parseObject(Type type, Object fieldName) {
int token = lexer.token();
if (token == JSONToken.NULL) {
lexer.nextToken();
return null;
}
if (token == JSONToken.LITERAL_STRING) {
if (type == byte[].class) {
byte[] bytes = lexer.bytesValue();
lexer.nextToken();
return (T) bytes;
}
if (type == char[].class) {
String strVal = lexer.stringVal();
lexer.nextToken();
return (T) strVal.toCharArray();
}
}
ObjectDeserializer deserializer = config.getDeserializer(type);
try {
if (deserializer.getClass() == JavaBeanDeserializer.class) {
if (lexer.token()!= JSONToken.LBRACE && lexer.token()!=JSONToken.LBRACKET) {
throw new JSONException("syntax error,except start with { or [,but actually start with "+ lexer.tokenName());
}
return (T) ((JavaBeanDeserializer) deserializer).deserialze(this, type, fieldName, 0);
} else {
return (T) deserializer.deserialze(this, type, fieldName);
}
} catch (JSONException e) {
throw e;
} catch (Throwable e) {
throw new JSONException(e.getMessage(), e);
}
}
ObjectDeserializer用来实现特定类型的反序列化
获取某个类型的ObjectDeserializer,如果某个类型是JavaBean简单类型,说明可以直接反序列化,如果不是则需要继续分解
ObjectDeserializer deserializer = config.getDeserializer(type);
try {
if (deserializer.getClass() == JavaBeanDeserializer.class) {
if (lexer.token()!= JSONToken.LBRACE && lexer.token()!=JSONToken.LBRACKET) {
throw new JSONException("syntax error,except start with { or [,but actually start with "+ lexer.tokenName());
}
return (T) ((JavaBeanDeserializer) deserializer).deserialze(this, type, fieldName, 0);
} else {
return (T) deserializer.deserialze(this, type, fieldName);
}
} catch (JSONException e) {
throw e;
} catch (Throwable e) {
throw new JSONException(e.getMessage(), e);
}
判定某种具体类型,其具体解析是由JavaBeanDeserializer中方法deserialze实现的
protected <T> T deserialze(DefaultJSONParser parser, // Type type, // Object fieldName, // Object object, // int features, // int[] setFlags) {