不知道小伙伴们有没有这样的困扰,平常开发中写单测,要mock一个复杂的对象,并且也知道了该对象的toString格式数据(比如从日志中获取),但是该怎么构建这个对象呢?
如果是json格式可以直接通过json反序列化得到对象,那么toString格式如何反序列得到对象呢?
从反序列化原理来看,我们首先要解析出对象的一个个属性,toString对象属性格式为 k1=v1,k2=v2
,那么可以按照逗号 ,
作为分隔符解析出一个个token,注意一个token可以是基本类型的kv,比如 int/Interger/…/String
这种;也可以是对象类型,比如 object/array/list/map
等。解析出来token之后,基本类型的token可以直接通过反射将v设置到对象属性(Field)中;对象类型的token可以继续按照toString格式进行反序列化,直到全部数据都反序列化成功为止;针对 array/list/map
的数据要获取到对应元素的实际类型才能知道要反序列化的对象。对应的代码实现如下:
/**
* toString格式反序列化类
*
* @author luoxiangnan
* @date 2020-03-02
*/
public class ToStringUtils {
/**
* toString格式反序列化
*/
@SuppressWarnings("all")
public static <T> T toObject(Class<T> clazz, String toString) throws Exception {
if (Objects.isNull(clazz) || Objects.isNull(toString) || StringUtils.isEmpty(toString)) {
return clazz == String.class ? (T) toString : null;
} else if (TypeValueUtils.isBasicType(clazz)) {
return (T) TypeValueUtils.basicTypeValue(clazz, toString.trim());
}
toString = TokenUtils.cleanClassPrefix(clazz, toString.trim());
toString = StringUtils.removeStart(toString, "(").trim();
toString = StringUtils.removeEnd(toString, ")").trim();
String token = null;
T result = clazz.newInstance();
while (StringUtils.isNotEmpty(toString) && StringUtils.isNotEmpty(token = TokenUtils.splitToken(toString))) {
toString = StringUtils.removeStart(StringUtils.removeStart(toString, token).trim(), ",").trim();
// 解析k/v格式的属性名/值
Pair<String, String> keyValue = TokenUtils.parseToken(token);
Field field = FieldUtils.getField(clazz, keyValue.getKey(), true);
Object value = TypeValueUtils.buildTypeValue(field, keyValue.getValue());
FieldUtils.writeField(field, result, value, true);
}
return result;
}
/**
* 字符串解析类
*/
static class TokenUtils {
/**
* 清除类名前缀字符串
*/
static String cleanClassPrefix(Class clazz, String toString) {
String simpleName = clazz.getSimpleName();
if (clazz.getName().contains("$")) {
// 内部类需要按照内部类名字格式
String rowSimpleName = StringUtils.substringAfterLast(clazz.getName(), ".");
simpleName = StringUtils.replace(rowSimpleName, "$", ".");
}
return to