Just murmur

用 Jackson 將 JSON 反序列化成 Polymorphic 物件

問題

假設有兩種下列的 JSON 文件,依特定屬性的值決定該文件的作用:

{
    "action": "login",
    "userId": "xxx"
}
{
    "action": "sendMessage",
    "message": "user Message"
}

假如兩種 JSON 文件都有可能收到,最簡單的方法就是都轉成 Map,但是這樣就很不物件化,這時候就需要一種方法可以自動判斷並反序列化成對應的物件。

解法

Deserialize JSON with Jackson into Polymorphic Types - A Complete Example 這篇文章可以看到很多有用的例子。在這邊可以參考 Example 5,不過用不到 MixIn,不知道是不是因為我用的是 Jackson 2.6。

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;

public class JacksonTypeTest  {
    public static void main(String[] args) throws Exception {
        String sendMessageJson = "{ \"action\": \"sendMessage\", \"message\": \"testMessage\"}";
        String loginJson = "{ \"action\": \"login\", \"userId\": \"123456\"}";

        ObjectMapper mapper = new ObjectMapper();

        Action sendMessageAction = mapper.readValue(sendMessageJson, Action.class);
        Action loginAction = mapper.readValue(loginJson, Action.class);

        System.out.println(sendMessageAction.getClass().getName());
        System.out.println(loginAction.getClass().getName());
    }
}


@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "action")
@JsonSubTypes({
    @Type(value = SendMessageAction.class, name = "sendMessage"),
    @Type(value = LoginAction.class, name = "login")
})
abstract class Action {
    protected String action;

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }
}

class SendMessageAction extends Action {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

class LoginAction extends Action {
    private String userId;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }
} 

從上面範例的輸出可以看到反序列化後的物件其類別會對應到真正的類別,這樣接下來的程式就可以用物件化的方式來操作了。

另一個問題

雖然可以正確的反序列化了,不過如果嘗試將物件序列化成 JSON 文件會發現 action 這個屬性的資料被輸出了兩次。下一篇文章再處理吧!