Javaでよく使われるjsonシーケンス化ツールの一部の限界


Problem
JavaのUnit Testを書く場合、Unit Testが望む値は、一般にtest/resourcesディレクトリからexpectResult.jsonをツールで読み込み、jsonシーケンス化ツールで逆シーケンス化してUnit Testが望む結果を得、テストの実際の結果と比較する.ただし、逆シーケンス化で使用されるクラスがサードパーティ製ライブラリに由来する場合(コードは変更できません)、大きな問題が発生します.以下では、アリクラウドのStreamRecordクラスを逆シーケンス化する際に発生する問題について説明します.
StreamRecordクラスの定義は次のとおりです.
public class StreamRecord {

    public enum RecordType {
        /**
         * PUT  
         *         , Record        。
         */
        PUT,

        /**
         * UPDATE  
         *         , Record          。
         */
        UPDATE,

        /**
         * DELETE  
         *          。
         */
        DELETE
    }

    /**
     * Record   
     */
    private RecordType recordType;

    /**
     *       
     */
    private PrimaryKey primaryKey;

    /**
     *         
     */
    private RecordSequenceInfo sequenceInfo;

    /**
     *  Record      , RecordColumn  
     */
    private List columns;

    /**
     *   Record   
     * @return Record   
     */
    public RecordType getRecordType() {
        return recordType;
    }

    public void setRecordType(RecordType recordType) {
        this.recordType = recordType;
    }

    /**
     *         
     * @return       
     */
    public PrimaryKey getPrimaryKey() {
        return primaryKey;
    }

    public void setPrimaryKey(PrimaryKey primaryKey) {
        this.primaryKey = primaryKey;
    }

    /**
     *          
     * @return        
     */
    public RecordSequenceInfo getSequenceInfo() {
        return sequenceInfo;
    }
    public void setSequenceInfo(RecordSequenceInfo sequenceInfo) {
        this.sequenceInfo = sequenceInfo;
    }

    /**
     *    Record        
     * @return  Record        
     */
    public List getColumns() {
        if (columns != null) {
            return columns;
        } else {
            return new ArrayList();
        }
    }

    public void setColumns(List columns) {
        this.columns = columns;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[RecordType:]");
        sb.append(this.recordType);
        sb.append("
[RecordSequenceInfo:]"); sb.append(this.sequenceInfo); sb.append("
[PrimaryKey:]"); sb.append(this.primaryKey); sb.append("
[Columns:]"); for (RecordColumn column : this.getColumns()) { sb.append("("); sb.append(column); sb.append(")"); } return sb.toString(); } }

本工程では当初Jacksonのみを用いてシーケンス化と逆シーケンス化を行っていたが,JacksonのObjectMapperはこのようなものを逆シーケンス化する際にNo suitable constructorのエラーを報告し,調査の結果Jacksonの逆シーケンス化にはデフォルトのコンストラクタ(パラメータ付きコンストラクタがあれば@JsonCreatorでコンストラクタを修飾し,@JsonPropertyでコンストラクタパラメータを修飾する)が必要であることが分かった.上記の類はなく、私たちがあってもアリ雲などのサードパーティライブラリを変更することができず、Jacksonを放棄し、アリ自身のfastjsonを考慮した.fastjsonは確かにこのクラスを逆シーケンス化することができますが、逆シーケンス化後のオブジェクトをよく分析すると、いくつかの深いフィールドの値がnullであることがわかりました.また、fastjsonは逆シーケンス化されたクラスに対して関数を構築する要求はありませんが、フィールドに要求があり、逆シーケンス化されたprivateフィールドにはsetterメソッドが必要で、正常な逆シーケンス化が可能であることがわかりました.(またはすべてのフィールドパラメータを持つコンストラクション関数があります)privateフィールドにsetterメソッドが欠けている場合、このフィールドの値はデフォルトです.最後にGoogleのGsonを使用することを考慮します.Gsonには上記の問題はありませんが、逆シーケンス化クラスにObjectタイプのフィールドがあり、そのフィールドの値が数値型である場合、GsonはDouble型に変わります.たとえば、フィールドが
private Map map;

jsonファイル:
{
  "age": 24,
  "height": 1.81
}

上記のjsonファイルをmapフィールドに逆シーケンス化すると、直感的には「age」フィールドの値のタイプはIntegerまたはLong型であるべきだと考えられますが、Gsonはここでは少し異常です.mapのvalueはObjectタイプであるため、具体的な数値タイプが明確に指定されていないため、keyが「age」のフィールドはDoubleタイプになります(私たちが直感的に望んでいるIntegerまたはLong型ではありません)Gsonのこの「特性」については、https://github.com/google/gso...の「debate」を参考にして、面白い「ネットユーザーの作者」を挙げることができます.
私の最終的な解決策はGsonで逆シーケンス化し、反射ツールReflectionTestUtils.setFieldを再利用していくつかの数値をDoubleからLongに変換することです.
Conclusion
  • Jacksonは強力ですが、逆シーケンス化されたクラスに対する要求は高いです(デフォルトのconstructorが必要です).
  • Fastjsonは速度が速いが、逆シーケンス化されたクラスにも一定の要求があり、逆シーケンス化が複雑なjsonではバグが多い(アリクラウドの多くのsdkもこのためfastjsonは使用されていない).
  • Gsonは比較的包括的であり、逆シーケンス化されたクラスに対する要求は最も低いが、Objectタイプの数値フィールド処理には友好的ではない.
  • 以上の実験で使用したバージョン:
    compile group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
    compile group: 'com.alibaba', name: 'fastjson', version: '1.2.56'

    したがって、サードパーティ製ライブラリをシーケンス化および逆シーケンス化するモデルが存在しない場合(コードが変更できない場合)、Jacksonが優先され、そうでない場合はGsonが選択されます.