FastJSONソース分析

11910 ワード

Gitリンク
FastjsonはJava言語で作成された高性能なJSONライブラリです。「規則的で迅速な整合を仮定する」アルゴリズムを採用して、JSON Parseの性能を極限まで高めて、現在Java言語の中で一番速いJSONライブラリです。Fastjsonインターフェースは簡単で使いやすいです。キャッシュのプログレッシブ、プロトコルのインタラクション、Web出力、Androidクライアントなどの様々なアプリケーションシーンで広く使われています。
git:https://github.com/alibaba/fastjson
Samples:https://github.com/alibaba/fastjson/wiki/Samples-DataBind
テストコード
@Test
public void test() {
    User user = new User(0L, "json");
    String jsonString = JSON.toJSONString(user);
    System.out.println(jsonString);
    User parseUser = JSON.parseObject(jsonString, User.class);
    System.out.println(parseUser);
}
public class User {
    private Long   id;
    private String name;
    
    public User(long id, String name) {
        this.id = id;
        this.name = name;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + "]";
    }
}
出力:
{"id":0,"name":"json"}
User [id=0, name=json]
プログレッシブ、反プログレッシブプロセス
プログレッシブプロセス
オブジェクトの例によれば、オブジェクトのクラスを取得し、Object Serializerインターフェースを実現するクラスを判断し、インターフェースを呼び出す方法writeで、オブジェクトのインスタンスをStringに変換する。
アンチプログレッシブプロセス
入力されたクラスClass clazzに基づいて、Object Deserializerを実現するクラスを判断し、インターフェースを呼び出す方法deserialzeは、Stringを対象インスタンスに変換する。
序列化のソースコード分析
最終的に関数に呼び出します。
public static String toJSONString(Object object, // 
                                  SerializeConfig config, // 
                                  SerializeFilter[] filters, // 
                                  String dateFormat, //
                                  int defaultFeatures, // 
                                  SerializerFeature... features) {
    // out            ,       string(return out.toString();)
    SerializeWriter out = new SerializeWriter(null, defaultFeatures, features);
    try {
        //       
        JSONSerializer serializer = new JSONSerializer(out, config);
        
        if (dateFormat != null && dateFormat.length() != 0) {
            serializer.setDateFormat(dateFormat);
            serializer.config(SerializerFeature.WriteDateUseDateFormat, true);
        }
        if (filters != null) {
            for (SerializeFilter filter : filters) {
                serializer.addFilter(filter);
            }
        }
        //     object     string,       out  
        serializer.write(object);
        return out.toString();
    } finally {
        out.close();
    }
}
writeの方法は以下の通りである。
public final void write(Object object) {
    if (object == null) {
        out.writeNull();
        return;
    }
    Class> clazz = object.getClass();
    /**
     *          ,           ObjectSerializer
     * 
     *     bool,     BooleanCodec  
     *     bool[],     PrimitiveArraySerializer  
     */
    ObjectSerializer writer = getObjectWriter(clazz);
    try {
        //    write   ,     ,   out    !
        writer.write(this, object, null, null, 0);
    } catch (IOException e) {
        throw new JSONException(e.getMessage(), e);
    }
}
テストケースでは、クラスJavaBeanSerializerが使用されており、プロビジョニングされるのは、カスタムJavaBeanクラスUserである。
Serialize Config
このうち、put関数はキーのペアをIdentityHashMap serializersに入れる。
public SerializeConfig(int tableSize, boolean fieldBase) {
    this.fieldBased = fieldBase;
    serializers = new IdentityHashMap(tableSize);
    
    try {
        if (asm) {
            asmFactory = new ASMSerializerFactory();
        }
    } catch (Throwable eror) {
        asm = false;
    }
    put(Boolean.class, BooleanCodec.instance);
    put(Character.class, CharacterCodec.instance);
    put(Byte.class, IntegerCodec.instance);
    put(Short.class, IntegerCodec.instance);
    put(Integer.class, IntegerCodec.instance);
    put(Long.class, LongCodec.instance);
    put(Float.class, FloatCodec.instance);
    put(Double.class, DoubleSerializer.instance);
    put(BigDecimal.class, BigDecimalCodec.instance);
    put(BigInteger.class, BigIntegerCodec.instance);
    put(String.class, StringCodec.instance);
    put(byte[].class, PrimitiveArraySerializer.instance);
    put(short[].class, PrimitiveArraySerializer.instance);
    put(int[].class, PrimitiveArraySerializer.instance);
    put(long[].class, PrimitiveArraySerializer.instance);
    put(float[].class, PrimitiveArraySerializer.instance);
    put(double[].class, PrimitiveArraySerializer.instance);
    put(boolean[].class, PrimitiveArraySerializer.instance);
    put(char[].class, PrimitiveArraySerializer.instance);
    put(Object[].class, ObjectArrayCodec.instance);
    put(Class.class, MiscCodec.instance);
    //   ……
    put(LinkedList.class, CollectionCodec.instance);
}
コードを分析:
put(Boolean.class, BooleanCodec.instance);
クラスBoolean CodecはインターフェースObject Serializerを継承しています。Object Deserializer、二つのインターフェース:
public interface ObjectSerializer {
    
    /**
     * fastjson invokes this call-back method during serialization when it encounters a field of the
     * specified type.
     * @param serializer 
     * @param object src the object that needs to be converted to Json.
     * @param fieldName parent object field name
     * @param fieldType parent object field type
     * @param features parent object field serializer features
     * @throws IOException
     */
    void write(JSONSerializer serializer, //
               Object object, //
               Object fieldName, //
               Type fieldType, //
               int features) throws IOException;
}
public interface ObjectDeserializer {
    /**
     * fastjson invokes this call-back method during deserialization when it encounters a field of the
     * specified type.
     * 

In the implementation of this call-back method, you should consider invoking * {@link JSON#parseObject(String, Type, Feature[])} method to create objects * for any non-trivial field of the returned object. * * @param parser context DefaultJSONParser being deserialized * @param type The type of the Object to deserialize to * @param fieldName parent object field name * @return a deserialized object of the specified type which is a subclass of {@code T} */ T deserialze(DefaultJSONParser parser, Type type, Object fieldName); int getFastMatchToken(); }

Boolean Codecのwriteは以下のように実現され、パラメータserializerにはoutのSerializerWriterオブジェクトが含まれ、outは最終的に出力のstringに変換される。
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
    SerializeWriter out = serializer.out;
    Boolean value = (Boolean) object;
    if (value == null) {
        out.writeNull(SerializerFeature.WriteNullBooleanAsFalse);
        return;
    }
    if (value.booleanValue()) {
        out.write("true");
    } else {
        out.write("false");
    }
}
Boolean Codecのdeserialzeは次のように実現します。lexer.token()がtrue、1等であれば、trueに戻ります。クラスJSONTokenには各種文字の定義があります。
@SuppressWarnings("unchecked")
public  T deserialze(DefaultJSONParser parser, Type clazz, Object fieldName) {
    final JSONLexer lexer = parser.lexer;
    Boolean boolObj;
    try {
        if (lexer.token() == JSONToken.TRUE) {
            lexer.nextToken(JSONToken.COMMA);
            boolObj = Boolean.TRUE;
        } else if (lexer.token() == JSONToken.FALSE) {
            lexer.nextToken(JSONToken.COMMA);
            boolObj = Boolean.FALSE;
        } else if (lexer.token() == JSONToken.LITERAL_INT) {
            int intValue = lexer.intValue();
            lexer.nextToken(JSONToken.COMMA);
            if (intValue == 1) {
                boolObj = Boolean.TRUE;
            } else {
                boolObj = Boolean.FALSE;
            }
        } else {
            Object value = parser.parse();
            if (value == null) {
                return null;
            }
            boolObj = TypeUtils.castToBoolean(value);
        }
    } catch (Exception ex) {
        throw new JSONException("parseBoolean error, field : " + fieldName, ex);
    }
    if (clazz == AtomicBoolean.class) {
        return (T) new AtomicBoolean(boolObj.booleanValue());
    }
    return (T) boolObj;
}
反プロローグ
アンチプログレッシブstringが対象になると、以下の関数が呼び出されます。重要なのは25行目のコードで、入力されたinput文字列を対応するクラスタイプに変換します。
@SuppressWarnings("unchecked")
public static  T parseObject(String input, Type clazz, ParserConfig config, ParseProcess processor,
                                      int featureValues, Feature... features) {
    if (input == null) {
        return null;
    }
    if (features != null) {
        for (Feature feature : features) {
            featureValues |= feature.mask;
        }
    }
    DefaultJSONParser parser = new DefaultJSONParser(input, config, featureValues);
    if (processor != null) {
        if (processor instanceof ExtraTypeProvider) {
            parser.getExtraTypeProviders().add((ExtraTypeProvider) processor);
        }
        if (processor instanceof ExtraProcessor) {
            parser.getExtraProcessors().add((ExtraProcessor) processor);
        }
        if (processor instanceof FieldTypeResolver) {
            parser.setFieldTypeResolver((FieldTypeResolver) processor);
        }
    }
    //        
    T value = (T) parser.parseObject(clazz, null);
    parser.handleResovleTask(value);
    parser.close();
    return (T) value;
}
T value=(T)parser.parseObject(clazz,null)のコードは以下の通りです。本例のUserオブジェクトに対しては、最終的にObject DeserialzerはJavaBenDeserializerのように位置づけられます。そのdeserialzeコードは少し長いので、貼り付けません。
@SuppressWarnings("unchecked")
public  T parseObject(Type type, Object fieldName) {
    int token = lexer.token();
    if (token == JSONToken.NULL) {
        lexer.nextToken();
        return null;
    }
    if (token == JSONToken.LITERAL_STRING) {
        if (type == byte[].class) {
            byte[] bytes = lexer.bytesValue();
            lexer.nextToken();
            return (T) bytes;
        }
        if (type == char[].class) {
            String strVal = lexer.stringVal();
            lexer.nextToken();
            return (T) strVal.toCharArray();
        }
    }
    //      class  ,         
    ObjectDeserializer derializer = config.getDeserializer(type);
    try {
        return (T) derializer.deserialze(this, type, fieldName);
    } catch (JSONException e) {
        throw e;
    } catch (Throwable e) {
        throw new JSONException(e.getMessage(), e);
    }
}
その他の用法
fastjsonを利用して、「1 2 3 5」または「1,2,3,4,5」という文字列を配列に変換することができます。実際の応用の中で、とても便利で簡潔です。
@Test
public void testArray() {
    String arrayString = "1 2 3 4 5";
    int[] array1 = JSON.parseObject("[" + arrayString + "]", int[].class);
    System.out.println(Arrays.toString(array1));
    
    String boolString = "1, true, 0, false";
    boolean[] array2 = JSON.parseObject("[" + boolString + "]", boolean[].class);
    System.out.println(Arrays.toString(array2));
}
fastjsonの整理思想
全体としては、2つのインターフェースが定義されています。Object SerializerとObject Deserializer。異なるクラスに対しては、異なるプログレッシブおよびアンチプログレッシブ方式が実現され、クラスの情報に基づいて適切なインターフェース実装クラスを選択することができる。
締め括りをつける
プロローグ、アンチプロローグを通してJava Beanオブジェクトを列化し、fastjsonのソースコードを分析します。多くのところで討論が行われていません。少なくともブログの形式で書くことができません。具体的な詳細が多すぎます。何か問題があれば、ご指摘ください。
Lと呼ばれる男性のリンク:https://www.jianshu.com/p/bc4c10860297 出所:簡書簡書の著作権は著者の所有になります。いかなる形式の転載も作者に連絡して授権を得て、出所を明記してください。