JAX-RS 2.0 與 XML/JSON 資料轉換 - 神奇的 MOXy & Jackson by MonsterSupreme | CodeData
top

JAX-RS 2.0 與 XML/JSON 資料轉換 - 神奇的 MOXy & Jackson

分享:

在前幾次的 JAX-RS 2.0 文章之中,介紹的都是 Java 基本型別的處理方式,不過我們一般比較常用的,反而是各種自訂 JavaBeans 型別與 XML/JSON 資料格式之間的轉換。資料轉換在 JAX-RS 2.0 裡頭,雖然不是預設就會啟用的功能,但是也不難。

建立 JAX-RS 2.0 專案

為了示範方便,請執行底下的指令,建立一個使用 Grizzly 的 Jersey 專案:

mvn archetype:generate 
-DarchetypeGroupId=org.glassfish.jersey.archetypes
-DarchetypeArtifactId=jersey-quickstart-grizzly2
-DarchetypeVersion=2.5.1
-DgroupId=tw.com.codedata
-DartifactId=jaxbdemo
-Dpackage=tw.com.codedata.jaxbdemo
-DinteractiveMode=false

刪除自動產生的 MyResource.javaMyResourceTest.java 檔案,然後補上底下的
Region.javaRegionService.java 檔案。

Region 類別是我們定義的 Resource,內容如下:

package tw.com.codedata.jaxbdemo;

import java.io.*;

public class Region implements Serializable
{
    static final long serialVersionUID = 20140124L;

    private int regionId;
    private String regionDescription;

    public Region() {}

    public Region(int regionId, String regionDescription)
    {
        this.regionId = regionId;
        this.regionDescription = regionDescription;
    }

    public int getRegionId()
    {
        return regionId;
    }

    public void setRegionId(int regionId)
    {
        this.regionId = regionId;
    }

    public String getRegionDescription()
    {
        return regionDescription;
    }

    public void setRegionDescription(String regionDescription)
    {
        this.regionDescription = regionDescription;
    }
}

RegionService 類別會將 Region 對外展現為 RESTful Service,內容如下:

package tw.com.codedata.jaxbdemo;

import java.util.*;
import javax.ws.rs.*;
import javax.ws.rs.core.*;

@Path("/regions")
public class RegionService
{
    private static List<Region> regionList = null;
    private static Region errorRegion = null;

    static 
    {
        regionList = new ArrayList<Region>();
        regionList.add(new Region(1, "Eastern"));
        regionList.add(new Region(2, "Western"));
        regionList.add(new Region(3, "Northern"));
        regionList.add(new Region(4, "Southern"));
        errorRegion = new Region(0, "Error");
    }

    public RegionService() 
    {
    }

    @POST
    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})    
    public int create(Region region)
    {       
        regionList.add(region);
        return 1; 
    }

    @GET
    @Path("/{regionId}")
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Region retrieve(@PathParam("regionId") int regionId)
    {        
        for (Region region : regionList)
        {
            if (region.getRegionId() == regionId) return region;
        }

        return errorRegion; 
    }

    @DELETE
    @Path("/{regionId}") 
    public int delete(@PathParam("regionId") int regionId)
    {   
        int found = 0;
        for (Region r : regionList)
        {
            if (r.getRegionId() == regionId) 
            {
                regionList.remove(r);
                found = 1;
            }
        }
        return found; 
    }

    @GET
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public List<Region> retrieveAll()
    {        
        return regionList; 
    }

    @PUT
    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})     
    public int update(Region region)
    {        
        int found = 0;
        int targetId = region.getRegionId();

        for (Region r : regionList)
        {
            if (r.getRegionId() == targetId) 
            {
                r.setRegionDescription(region.getRegionDescription());;
                found = 1;
            }
        }
        return found; 
    }
}

整個專案目錄架構如下:

jaxbdemo Tree View

執行之後,雖然可以透過 RESTClient 看到 WADL (Web Application Description Language) 檔案:

WADL Output

但是卻沒辦法正確觸發 regions,取得所有 Region 資訊,XML 跟 JSON 都一樣:

JSON Output Error

原因就在於,Jersey 目前的設定只能處理簡單的 Java 型別,但是對於 POJO 與 XML/JSON 格式之間的轉換,就無能為力了。

XML 支援

Java 與 REST 的邂逅(二)JAX-RS 核心 Annotation 裡頭提到,如果想要提供 XML 支援,也就是讓 @Consumes@Produces 能夠接受與產生 XML 資料,有底下幾種作法:

每個resource method可以最多允許一個沒有annotation的參數,這代表的是處理request body(entity)的參數。而MIME type跟Java type有一些法則可以參考。下面是一個表單:

XML media types (text/xml, application/xml and application/…+xml)

javax.xml.transform.Source
javax.xml.bind.JAXBElement
Application supplied JAXB classes (types annotated with @XmlRootElement or @XmlType)

JAX-RS 2.0 的規格要求 Implementation 必須支援上面提到的幾種作法。不過 JAXP 的 Source 與 JAXB 的 JAXBElement 比較低階一點,必須要寫點程式碼,所以通常都用在需要客製化或是微調的場合。

Application supplied JAXB classes 指的就是有加上 JAXB 相關 Annotation 的 Java 類別。這種方式比較簡單,適合用在初學或是沒有詭異需求的時候。

以我們的範例來說,只要修改一下前面的 Region 類別,加入 JAXB 相關的 Annotation,內容如下:

package tw.com.codedata.jaxbdemo;

import java.io.*;
import javax.xml.bind.annotation.*;

@XmlRootElement(name = "region")
@XmlAccessorType(XmlAccessType.FIELD)
public class Region implements Serializable
{
    ...
}

Jersey 就可以順利進行 Marshalling 跟 Unmarshalling,把 Region 類別的物件轉換為 XML,或是把 XML 資料轉換為 Region 類別的物件。

XML Output

JSON 支援

Jersey 把 JSON 這部份的支援設計成 Extension Module,目前可以透過底下任意一個 3rd-Party Library 來達成:

  • MOXy
  • Java API for JSON Processing (JSON-P)
  • Jackson
  • Jettison

只要 Jersey 在 CLASSPATH 發現這幾個 Library 的存在,就會自動註冊使用。

MOXy

因為 EclipseLink 貢獻了 EclipseLink MOXy 這個 JSON-Binding Provider 給 JAX-RS 2.0,而 Jersey 又是 JAX-RS 2.0 的 Reference Implementation,所以 MOXy 順理成章就變成是 Jersey 建議使用的 JSON-Binding Library。

檢查剛剛由 Maven 自動產生的 pom.xml 檔案,會看到裡面有一段 <dependency> 的設定很微妙:

<!-- uncomment this to get JSON support:
 <dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-moxy</artifactId>
</dependency>
-->

打開這一段註解,重新再編譯一次,執行後應該就可以順利觸發 regions 取得 JSON 格式資訊:

JSON Output

Jackson

網路上有些人比較喜歡使用 Jackson 這個 JSON Processor,所以 Jersey 當然也支援。Jackson 有一個 Jackson JAX-RS Providers 子計畫,用來為 JAX-RS 2.0 提供 JSON、XML、與 Smile 三種資料格式的轉換。從 Jackson 2.2 版開始,這個子計畫會取代以前的 Extension Module,比方說 Jackson JAX-RS JSON Provider

透過 Maven 使用 Jackson JAX-RS Providers 的時候,請在專案的 pom.xml 檔案,加入以下的 <dependency>

<dependency>
    <groupId>com.fasterxml.jackson.jaxrs</groupId>
    <artifactId>jackson-jaxrs-json-provider</artifactId>
    <version>2.3.0</version>
</dependency>

然後一樣為 Region 類別加入 JAXB 相關的 Annotation,就能夠同時支援 JSON 與 XML 資料轉換。

檢視 jackson-jaxrs-json-provider 本身的 pom.xml 檔案,其實也就不難發現,為什麼它也會支援 XML 資料轉換,關鍵就在最後面的 jackson-module-jaxb-annotations <dependency>

<dependencies>
    <!-- builds on shared base JAX-RS handling code... -->
    <dependency>
        <artifactId>jackson-jaxrs-base</artifactId>
        <groupId>${project.groupId}</groupId>
        <version>${project.version}</version>
    </dependency>
    <!-- Extends Jackson core, mapper, and also (sort of optionally) on JAXB annotation handler -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>${version.jackson.core}</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>${version.jackson.core}</version>
    </dependency>
    <!-- also need JAXB annotation support -->
    <dependency>
        <groupId>com.fasterxml.jackson.module</groupId>
        <artifactId>jackson-module-jaxb-annotations</artifactId>
        <version>${version.jackson.jaxb}</version>
    </dependency>
    <!-- test deps should come from parent, including jersey -->
</dependencies>

後續 >> JAX-RS 2.0 與 JSONP

分享:
按讚!加入 CodeData Facebook 粉絲群

相關文章

留言

留言請先。還沒帳號註冊也可以使用FacebookGoogle+登錄留言

關於作者

目前從事教育訓練工作。自認為會的技術不多,但是學不會的也不多,最擅長把老闆交代的工作,以及找不到老師教的技術,想辦法變成自己的專長。

熱門論壇文章

熱門技術文章