简介:
通过SPEL 和 fastjson 配合使用,指定JSON中字段解析的表达式,实现指定字段按照指定表达式填充的效果。
以使用三目运算符指定解析为例,代码如下。
首先,定义字段的反序列化解析类 SpelExpressDeserializer
import java.lang.reflect.Type;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.deserializer.ContextObjectDeserializer;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import lombok.extern.slf4j.Slf4j;
/**
* SPEL运算反序列
*
* @author jun.chen
*
*/
@Slf4j
public class SpelExpressDeserializer extends ContextObjectDeserializer implements ObjectDeserializer {
private ExpressionParser spel = new SpelExpressionParser();
@Override
public int getFastMatchToken() {
return 0;
}
@SuppressWarnings("unchecked")
@Override
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName, String format, int features) {
Object parse = parser.parse(fieldName);
if (null == parse || StringUtils.isEmpty(format)) {
return null;
}
// 通过SPEL运算出结果。
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariables(JSON.parseObject(parser.getInput()));
T value = null;
try {
String express = format.replaceAll("'", "\"");
Expression exp = spel.parseExpression(express);
value = (T) exp.getValue(context, Class.forName(type.getTypeName()));
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return value;
}
}
然后,单元测试如下
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.junit.Test;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
public class JsonFormatWithSpelTest {
@Data
static class DemoBill {
/** 单据编号 */
private String billNo;
/** 备注消息 */
private String remark;
/** 是否包含备注 */
@JSONField(alternateNames = "remark", format = "#remark == null || #remark.isEmpty()? false:true", deserializeUsing = SpelExpressDeserializer.class)
private Boolean has_remark;
/** 备注长度 */
@JSONField(alternateNames = "remark", format = "#remark == null || #remark.isEmpty()? 0:#remark.length()", deserializeUsing = SpelExpressDeserializer.class)
private Integer remark_length;
}
@Test
public void testDeserializer() {
String json_a = "{\"billNo\":\"No0001\",\"remark\":\"1234\",\"_remark\":\"1234\"}";
DemoBill demo_a = fillJson(json_a, DemoBill.class);
System.out.println("demo_a: " + JSON.toJSONString(demo_a));
String json_b = "{\"billNo\":\"No0002\"}";
DemoBill demo_b = fillJson(json_b, DemoBill.class);
System.out.println("demo_b: " + JSON.toJSONString(demo_b));
}
/**
* 将JSON字符串反序列化为Bean对象
*
* @param <T>
* @param sourceJson
* @param clazz
* @return
*/
private <T> T fillJson(String sourceJson, Class<T> clazz) {
Map<String, List<String>> fieldMap = getNameMapping(clazz);
JSONObject json = JSON.parseObject(sourceJson);
// 填充自定义标签字段值,用关联字段填充,因为JSON数据中不包含当前属性时,反序列化方法不会执行。
for (Entry<String, List<String>> entry : fieldMap.entrySet()) {
String key = entry.getKey();
Object val = json.containsKey(key) ? json.get(entry.getKey()) : "";
entry.getValue().forEach(a -> json.put(a, val));
}
return JSON.parseObject(json.toJSONString(), clazz);
}
/**
* 抽取类中指定使用SpelExpressDeserializer.class进行反序列化的字段和关联字段的关系
*
* @param clazz
* @return key:关联字段 value:与关联字段挂钩的解析字段
*/
private Map<String, List<String>> getNameMapping(Class<?> clazz) {
Map<String, List<String>> fieldMap = new HashMap<>();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
declaredField.isAnnotationPresent(JSONField.class);
JSONField annotation = declaredField.getAnnotation(JSONField.class);
if (null == annotation) {
continue;
}
if (annotation.deserializeUsing().equals(SpelExpressDeserializer.class)) {
String name = declaredField.getName();
String refField = annotation.alternateNames()[0];
List<String> list = fieldMap.get(refField);
if (null == list) {
list = new ArrayList<>();
}
list.add(name);
fieldMap.put(refField, list);
}
}
return fieldMap;
}
}
说明:JSON字符串中不包含某个属性时,不会进入反序列化方法
打印结果如下。
demo_a: {"billNo":"No0001","has_remark":true,"remark":"1234","remark_length":4}
demo_b: {"billNo":"No0002","has_remark":false,"remark_length":0}