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);
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM