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);
	}
}
posted @ 2021-10-28 22:31  一杯半盏  阅读(1279)  评论(0编辑  收藏  举报