Fork me on GitHub

自定义fastjson对枚举类型的序列化及反序列化过程

通常,fastjson在序列化及反序列化枚举时,一般以下几种策略:

1).根据枚举的name值序列化及反序列化(默认)

2).根据枚举的ordinal序列化及反序列化

3).根据枚举的toString方法序列化,但是反序列仍采取默认的策略

 

这显然对我们的业务处理不够灵活,考虑以下一种情况:

有一个文章类,它有标题,内容等属性,其中有一个属性是枚举类,表示文章是否通过审核。

如下:

public class Article {private String title;

    private String content;private AuditStatus status;public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }public AuditStatus getStatus() {
        return status;
    }

    public void setStatus(AuditStatus status) {
        this.status = status;
    }
}

对应的枚举类型,它包含一个标志状态的code:

public enum AuditStatus {
    AUDITING(1), PASSED(2), FAILED(3);

    private int code;

    AuditStatus(int code){
        this.code = code;
    }

    public int getCode(){
        return code;
    }
    public static AuditStatus convert(int code){
        AuditStatus[] enums = AuditStatus.values();
        for(AuditStatus e : enums){
            if(e.code == code)
                return e;
        }
        return null;
    }
}

注意,上述的code并不等于枚举的ordinal。

我们希望AuditStatus序列化和反序列化的都是根据其code值来。

比如序列化成如下字符串:

{"content":"This is content","status":1,"title":"Article 1"} 

并能正确反序列化得到的Article对象中的status属性是AuditStatus.AuditStatus。

 

我们逐个对应上述的策略来看看是否能满足我们的要求:

方法一:会将status序列化成AUDITING。因此不行

方法二:会将status根据ordinal来序列化,得到的结果为0,也不行。

方法三:我们可以override toString方法,这样可以是的序列化的结果正确,但是反序列化过程仍然不行。

 

那么没有其他方法可以满足我们的业务需求了嘛?

网上给出的一种方法如下,修改Article类:

 1 public class Article {
 2 
 3     private String title;
 4 
 5     private String content;
 6 
 7     private AuditStatus statusCode;
 8 
 9     public String getTitle() {
10         return title;
11     }
12 
13     public void setTitle(String title) {
14         this.title = title;
15     }
16 
17     public String getContent() {
18         return content;
19     }
20 
21     public void setContent(String content) {
22         this.content = content;
23     }
24 
25     @JSONField(serialize = false)
26     public AuditStatus getStatusCode() {
27         return statusCode;
28     }
29 
30     @JSONField(deserialize = true)
31     public void setStatusCode(AuditStatus statusCode) {
32         this.statusCode = statusCode;
33     }
34 
35     @JSONField(name = "status")
36     public int getStatus(){
37         return statusCode.getCode();
38     }
39 
40     @JSONField(name = "status")
41     public AuditStatus setStatus(int code){
42         return AuditStatus.convert(code);
43     }
44 }

这种方式屏蔽了默认的序列化及反序列化过程:行7(将status重命名成statusCode),行25-33(禁止了statusCode的序列化);并增加了自定义的序列化过程(行35-43)。

我测试了这种方式,虽然能在序列化时得到正常结果,但是却无法正常发序列化。

测试代码:

public class SerializeTest {
    public static void main(String[] args){
        Article article = new Article();
        article.setTitle("Article 1");
        article.setContent("This is content");
        article.setStatusCode(AuditStatus.AUDITING);

        String str = JSON.toJSONString(article);
        System.out.println(str);

        Article article1 = JSON.parseObject(str, Article.class);
        System.out.println(article1.getStatusCode());
    }
}

输出的结果:

因此这种做法并不可行,而且会让代码的可读性变差。

 

因此我推荐一种我尝试过成功的方法:编写自定义的编解码器,在通过@JsonField的serializeUsing和deserializeUsing属性指定编解码过程中使用的编解码器。

添加自定义的编解码器:

//ObjectSerializer和ObjectDeserializer分别是fastjson的编码器和解码器接口
public class AudtiStautsCodec implements ObjectSerializer, ObjectDeserializer {
    //反序列化过程
    public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
        Object value = parser.parse();
        return value == null ? null : (T) AuditStatus.convert(TypeUtils.castToInt(value));
    }

    //暂时还不清楚
    public int getFastMatchToken() {
        return 0;
    }

    //序列化过程
    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
        serializer.write(((AuditStatus)object).getCode());
    }
}

修改Article类,在status字段上增加@JsonField属性,并指定编解码器:

public class Article {

    private String title;

    private String content;

    @JSONField(serializeUsing = AudtiStautsCodec.class, deserializeUsing = AudtiStautsCodec.class)
    private AuditStatus status;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public AuditStatus getStatus() {
        return status;
    }

    public void setStatus(AuditStatus status) {
        this.status = status;
    }

}

在通过之前的测试代码测试一下结果是否正确:

public class SerializeTest {
    public static void main(String[] args){
        Article article = new Article();
        article.setTitle("Article 1");
        article.setContent("This is content");
        article.setStatus(AuditStatus.AUDITING);

        String str = JSON.toJSONString(article);
        System.out.println(str);

        Article article1 = JSON.parseObject(str, Article.class);
        System.out.println(article1.getStatus());
    }
}

结果如下:

 

posted @ 2018-08-22 09:31  insaneXs  阅读(26388)  评论(0编辑  收藏  举报