使用 Jackson 把 Java 對象轉換成 JSON 字串

原來 Java 項目中用的 JSON 組件庫主要是 Gson 和 json-lib,Gson 算是很錯的庫,json-lib 略顯寒磣。好啦,最近 Play 2.x 中棄用了 Gson 而採納了 Jackson,所以現在就來打探一下 Jackson,踩個點吧。

Jackson 號稱非常高的性能,聽說比另兩位兄弟 Gson 和 json-lib 高出一大截,我沒有親測,可是有心人做了,看這個鏈接 兩款JSON類庫Jackson與JSON-lib的性能對比(新增第三款測試) 中的數據。2010 年 8 月份的測試結果,不知現在隨着版本的變更是否仍然保持着這種懸殊。

通常我會在把文章開頭塞豐滿,做足前戲,並不是因爲在天涯混習慣了的緣故,況且我在天涯總是訥於言的; 在這裏,自己的地盤自己作主,不會有要求碼足多少字才能發表的自虐性需求,僅僅是讓本文在主頁上顯示時的的概要不空洞而已,可以簡單粗暴的稱之爲廢話。

了了,先了解 Jacson 最貼近實際應用場景的應用,即 Jackson 怎麼把一個 Java 生成對應的 JSON 字符串,看看前面的文字有這麼多了,直接上一段代碼吧,而後再慢慢假設與分解:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package cc.unmi.testjackson;
 
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
 
/**
 * @author Unmi
 */
public class TestClient {
 
    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
         
        mapper.setVisibility(JsonMethod.FIELD, Visibility.ANY);
        mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
         
        Person person = new Person("Unmi", new Address("JiangXi"));
         
        String json = mapper.writeValueAsString(person);
        System.out.println(json);
    }
}
 
class Person {
    private String name;
    private Address address;
 
    public Person(String name, Address address){
        this.name = name;
        this.address = address;
    }
}
 
class Address {
    private String province;
     
    public Address(String province){
        this.province = province;
    }
     
    public String getProvince(){
        return this.province;
    }
}

要使用 Jackson,自然要引入相應的 jar 包,用 Maven 管理依賴的在 pom.xml 中加上:

1
2
3
4
5
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-mapper-asl</artifactId>
        <version>1.9.11</version>
    </dependency>

這樣在項目中就會有了 jackson-mapper-asl-1.9.11.jar 和 jackson-core-asl-1.9.11.jar 這兩個 jar 包,其實當前版本是 2.1.0,只是 Maven 中央庫中還沒有。

上面代碼結果是可預測了,就是:

1
2
3
4
5
6
{
  "name" : "Unmi",
  "address" : {
    "province" : "JiangXi"
  }
}

Jackson 提供了 ObjectMapper 和 JsonGenerator 兩個 API 讓你去玩轉 JSON,其實內部實現肯定匯合在一起了,別讓兩個選擇就使你不會選擇了。

如果把第一段代碼中的第 20 行拿掉,運行會拋出異常:

Exception in thread “main” org.codehaus.jackson.map.JsonMappingException: No serializer found for class cc.unmi.testjackson.Person and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) )
    at org.codehaus.jackson.map.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:52)
    at org.codehaus.jackson.map.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:25)
    at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:610)
    at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:256)
    at org.codehaus.jackson.map.ObjectMapper._configAndWriteValue(ObjectMapper.java:2575)
    at org.codehaus.jackson.map.ObjectMapper.writeValueAsString(ObjectMapper.java:2097)

原因是 Jackson 默認情況下不知道怎麼去序列化,解決辦法是:

1. 用上面的第 20 行代碼,setVisibility,這個屬性也可以通過 SerializationConfig 來設置
2. 或是給你要轉換 Json 的類加上註解 @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY)3. 也可以給字段或 getter 上加上 @JsonProperty 註解,還能用它指定序列化時的屬性名

第 21 行是輔助設置,對格式的控制等,比要不要輸出 null 值的,數字日期怎麼顯示,多瞧瞧那些 Feature:

org.codehaus.jackson.map.SerializationConfig.Feature.*
org.codehaus.jackson.JsonGenerator.Feature.*
org.codehaus.jackson.JsonParser.Feature.*

Jackson 的定製性還是很強的。再來想像這麼一個需求,想輸出的 JSON 屬性也可定製,比如不想 {“name”: “Unmi”},想要屬性的第一個字母是大寫的,{“Name”: “Unmi”}。沒問題,用 ObjectMapper  的

setPropertyNamingStrategy(PropertyNamingStrategy s) 方法

定義自己的 PropertyNamingStrategry, 可以繼承自 PropertyNamingStrategy 或 PropertyNamingStrategyBase,顯然繼承自 PropertyNamingStrategyBase 來簡單,因爲它幫你適配了,只需覆寫它的一個方法,如:

01
02
03
04
05
06
07
08
09
10
11
import org.codehaus.jackson.map.PropertyNamingStrategy.PropertyNamingStrategyBase;
 
public class CapitalizedPropertyNamingStrategy extends PropertyNamingStrategyBase {
 
    @Override
    public String translate(String propertyName) {
        String name = propertyName.replaceAll("^\\w", propertyName.toUpperCase().substring(0,1));
        return name;
    }
 
}

然後對 mapper 調用

1
mapper.setPropertyNamingStrategy(new CapitalizedPropertyNamingStrategy());

再次運行代碼,結果就變成了:

1
2
3
4
5
6
{
  "Name" : "Unmi",
  "Address" : {
    "Province" : "JiangXi"
  }
}

還有什麼需求,混跡代碼中,最不能缺的就是想像力了。

小結一下:

1. ObjectMapper 可用來序列化和反序列化對象,而 JsonParser 是用來反序列化的,JsonGenerator 是序列化的。若想讓代碼職責分明,就用 JsonParser 或  JsonGenerator 代替 ObjectMapper 來用

2. ObjectMapper 和 JsonGenerator 有各種各樣的 writeXxx() 方法,可以讓結果輸出到不同的目的地; 同樣  ObjectMapper 和 JsonParser 也有各色 readXxx() 方法,方便你從不同的源獲得數據

3. 多看看 Jackson 爲我們提供了註釋 Annotation,留意上面那幾個 Feature 中有些什麼枚舉值,這裏面也是我們的擴展點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章