Jackson ObjectMapper JSON序列化工具使用笔记,由浅入深
Apache Dubbo 官方放弃了FastJson
Jackson目前是世界上最好的Java JSON库。
基本用法,ObjectNode, ArrayNode等 ValueNode
生成一个 jsonobject结构
ObjectNode protocol = new ObjectNode(JsonNodeFactory.instance);
//或者 ObjectNode protocol = JsonNodeFactory.instance.objectNode();
protocol.put("batchSize", 20480);
protocol.putArray("columns").add("id").add("name");
把数组、List作为一个带名称的 jsonarray 结构
List<xxx> costs = ...
ObjectNode x = JsonNodeFactory.instance.objectNode().putPOJO("costs", costs);
字符串反序列化为List、数组
//slankka提示1: 数据是List的
objectMapper.readValue(str, new TypeReference<List<Item>>() {});
序列化任意对象、数组
objectMapper.writeValueAsString(object);
JsonNode 操作
修改JsonNode
注意:需要强转ObjectNode
没必要因为个别字段不一致就专门定义一个POJO,太繁琐。
本身做Controller就是为了生成JSON,而JSON就是Javascript Object Notation
例如service 字段转成 codec里面的value值,根据fromId 生成一个新newRef值。
Map<String, String> codec ...
Map<Long, String> laborRefNames ...
JsonNode jsonNodes = new ObjectMapper().valueToTree(iterableVals);
jsonNodes.forEach(x -> ((ObjectNode) x)
.put("service", codec.get(x.get("service").asText()))
.put("newRef", laborRefNames.get(x.get("fromId").asLong())));
从JsonNode中获取JsonArray,再反序列化成对象
//slankka提示2:从JsonNode中获取一个JsonArray (JsonNode类型)
//slankka提示3:MetricsPack是一个ArrayList子类,与前文的TypeReference不同,这里是一个技巧。
metrics = objectMapper.treeToValue(jsonNode.get("metrics"), MetricsPack.class);
字符串反序列化为JsonNode,并不直接转换成对象
做接口设计的时候,比较好用,不关心具体的数据类差异,只关心数值字段
JsonNode jsonNode = objectMapper.readTree(str_input);
interface DataExtractor {
Object handle(JsonNode jsonNode);
}
DataExtractor instance;
instance.handle(jsonNode);
从JsonNode中获取数值
//十分灵活,需要字符串就是字符串,需要转成int就是int。(前提是数据类型比较符合,Javascript本身就是灵活的)
int instanceId = jsonNode.get("instanceId").asInt();
String text = jsonNode.get("instanceId").asText();
//Slankka提示4:一定要调用 asInt, asLong, asText,否则获取得到的是JsonNode, 他有toString,容易被误用。
Timestamp timestamp = Timestamp.from(Instant.ofEpochMilli(jsonNode.get("createTime").asLong()));
Optional.ofNullable(jsonNode.get("room")).map(JsonNode::asText).orElse(null)
使用JsonPath语法获取数值
使用JsonPath更为简洁,可以替代上述jsonNode.get()语法
slankka.setTopicName(jsonNode.at("/additional/topicName").asText());
slankka.setReplica((short) jsonNode.at("/additional/replica").asInt());
slankka.setUserId(jsonNode.at("/additional/userId").asInt());
slankka.setClusterId((short) jsonNode.at("/additional/clusterId").asInt());
slankka.setPartitionNum(jsonNode.at("/additional/partitionNum").asInt());
高级用法
使用泛型 + ArrayList子类 + 某种设计模式(模板模式?)
//抽象盒子
public abstract class PrototypeBox<T extends AbstractApp> extends ArrayList<T> {
String whatBoxCanDo() {
for (AbstractApp app : this) {
app.whatAppCanDo();
}
};
}
//抽象物体单元
public abstract class AbstractApp {
protect String platform;
protect Integer platId;
public abstract String whichIsDifferent();
public ObjectNode mayBeYouWantToSerialize() {
ObjectNode _dataNode = new ObjectNode(JsonNodeFactory.instance);
_dataNode.put("Question", "我们哪里不一样?" + whichIsDifferent() );
return _dataNode;
}
void whatAppCanDo() {
mayBeYouWantToSerialize();
};
}
//派生物体A
public class IosApp extends AbstractApp {
public static class Pack extends PrototypeBox<IosApp> {
}
}
//派生物体B
public class AndroidApp extends AbstractApp {
public static class Pack extends PrototypeBox<AndroidApp> {
}
}
//场景1
IosApp.Pack anApps = null;
try {
anApps = objectMapper.treeToValue(at, IosApp.Pack.class);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
//场景2
AndroidApp.Pack anApps = null;
try {
anApps = objectMapper.treeToValue(at, AndroidApp.Pack.class);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
//Slankka提示5:处理方式,由上到下,由外及内。
anApps.whatBoxCanDo();
这个例子最复杂,总而言之,其实就是两种对象数组(List),他们不一样,但是大部分属性,方法是共同的。
好处就是可以一并处理,各自再处理不同的细节。
这样设计,是服从高内聚,低耦合的原则的。
附录:
StringBoot中jackson的配置
可大写也可小写,可搜索spring.jackson.serialization查看SpringBoot Jar包源码支持的其他类型
例如
{
"name": "spring.jackson.serialization",
"type": "java.util.Map<com.fasterxml.jackson.databind.SerializationFeature,java.lang.Boolean>",
"description": "Jackson on\/off features that affect the way Java objects are serialized.",
"sourceType": "org.springframework.boot.autoconfigure.jackson.JacksonProperties"
}
例:
# 将日期类型序列化为TIMESTAMP时间戳字符串
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=true
# 将BIGDECIMAL序列化为 bigDecimal.toPlainString(), 防止生成科学计数法
spring.jackson.generator.WRITE_BIGDECIMAL_AS_PLAIN=true
※Spring把这个选项设置为false
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ObjectMapper.class)
public class JacksonAutoConfiguration {
private static final Map<?, Boolean> FEATURE_DEFAULTS;
static {
Map<Object, Boolean> featureDefaults = new HashMap<>();
featureDefaults.put(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
featureDefaults.put(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false);
FEATURE_DEFAULTS = Collections.unmodifiableMap(featureDefaults);
}
}