Official account: Java Xiaokaxiu, website: Javaxks.com

Author: LarvaZhang, link: www.cnblogs.com/larva-zhh/p…

Why fastjson

In the project, FastJSON is widely used as a serialization and deserialization framework, and even ORM relies on FastJSON to serialize and deserialize some fields. So why do you need to replace a heavily used infrastructure?

The reasons are as follows:

  1. Fastjson focuses too much on performance, not enough support for some advanced features, and some custom features completely deviate from JSON and JS specifications, resulting in incompatibility with other frameworks.
  2. Many fastjson documents are missing, some features are not even documented, and the code lacks annotations and is obscure;
  3. Fastjson CVE bug detection is weak, many CVE database websites about Fastjson CVE is very few, such as the recent AutoType caused by high-risk vulnerability, Although and Jackson PolymorphicDeserialization is the same bug, but almost no fastjson CVE website bug reports.

Selection of the framework

See mvnRepository JSON Libraries for a list of the top 10 frameworks in terms of popularity:

  • jackson2(com.fasterxml.jackson)
  • gson
  • org.json
  • jackson1(com.codehuas.jackson)
  • fastjson
  • cheshire
  • json-simple

Jackson1 is an outdated framework and can be ignored, Cheshire and JSON-Simple rank worse than Fastjson, and jackson2, gson, and org.json are also ignored. The usage of org.json is much less than that of Jackson2 and gson, so org.json can also be excluded.

There are a lot of articles comparing Jackson and Gson, stackOverflow search for them, and here are just a few blogs:

  • jackson vs gson
  • JSON in Java
  • the ultimate json library json-simple vs gson vs jackson vs json

Jackson is similar to Gson in terms of feature support, stability, scalability, ease of use, and community activity. You can refer to the Baeldung Jackson series and the Baeldung Gson series for tutorials. But Jackson has more readily available library compatibility support such as jackson-datatype-commons-lang3, as well as richer output dataformat support such as jackson-dataformatt-yaml, And the Spring framework uses Jackson by default, so I ended up using Jackson.

PS: Jackson 2.10.0 began experimenting with using a whitelist mechanism based on a new API to avoid RCE vulnerabilities. See github.com/FasterXML/j…

Replace fastjson

A common use scenario for Fastjson is serialization and deserialization, with occasional operations on JSONObject and JSONArray instances.

The source code analysis for the following steps is based on the following versions:

  • Fastjson v1.2.60
  • Jackson - core v2.9.9
  • Jackson - annotations v2.9.0
  • Jackson - databind v2.9.9.3

Deserialization

Fastjson json string deserialized into Java beans often use com. Alibaba. Fastjson. Json static methods (JSONObject, JSONArray static methods is also comes from the json), commonly used have the following API:

public static JSONObject parseObject(String text);

public static JSONObject parseObject(String text, Feature... features);

public static <T> T parseObject(String text, Class<T> clazz);

public static <T> T parseObject(String text, Class<T> clazz, Feature... features);

public static <T> T parseObject(String text, TypeReference<T> type, Feature... features);

public static JSONArray parseArray(String text);

public static <T> List<T> parseArray(String text, Class<T> clazz);
Copy the code

From the way into the can guess, fastjson when performing deserialization Parse behavior by com. Alibaba. Fastjson. Parser. The specified Feature. After studying the source code for parseObject, we found that the underlying methods are ultimately the following:

public static <T> T parseObject(String input, Type clazz, ParserConfig config, ParseProcess processor, int featureValues, Feature... features) { if (input == null) { return null; FeatureValues = featureValues = featureValues = featureValues = featureValues = 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; }Copy the code

After searching the usage through IDE, it was found that DEFAULT_PARSE_FEATURE was used as the benchmark resolution feature switch when featureValues was not included. Here is the instantiation code for the json.default_parse_feature:

static {
        int features = 0;
        features |= Feature.AutoCloseSource.getMask();
        features |= Feature.InternFieldNames.getMask();
        features |= Feature.UseBigDecimal.getMask();
        features |= Feature.AllowUnQuotedFieldNames.getMask();
        features |= Feature.AllowSingleQuotes.getMask();
        features |= Feature.AllowArbitraryCommas.getMask();
        features |= Feature.SortFeidFastMatch.getMask();
        features |= Feature.IgnoreNotMatch.getMask();
        DEFAULT_PARSER_FEATURE = features;
}
Copy the code

Fastjson also reads configuration from environment variables to modify DEFAULT_PARSER_FEATURE(although this is rarely done), but it’s best to verify the actual parsing feature switch in your environment by actually running the program.

@Test public void printFastJsonDefaultParserFeature() { for (Feature feature : Feature.values()) { if (Feature.isEnabled(JSON.DEFAULT_PARSER_FEATURE, feature)) { System.out.println(feature); }}}Copy the code

Fastjson and Jackson deserialization feature comparison table

Deserialization fastjson and the characteristics of Jackson TestCase DeserializationUseJacksonReplaceFastJsonTest. Java

Serialization

Fastjson Java Bean serialization into a json string is usually use com. Alibaba. Fastjson. Json static methods (JSONObject, JSONArray static methods is also comes from the json). The following apis are commonly used:

public static String toJSONString(Object object);

public static String toJSONString(Object object, SerializerFeature... features);

public static String toJSONStringWithDateFormat(Object object, String dateFormat, SerializerFeature... features);

public static String toJSONString(Object object, boolean prettyFormat);

public static void writeJSONString(Writer writer, Object object, SerializerFeature... features);
Copy the code

The fastjson feature is controlled by the SerializerFeature during serialization. After looking at the source code of toJSONString, it is found that the following methods are eventually called:

public static String toJSONString(Object object, SerializeConfig config, SerializeFilter[] filters, String dateFormat, int defaultFeatures, SerializerFeature... features) { 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); } } serializer.write(object); return out.toString(); } finally { out.close(); }}Copy the code

After searching usage through IDE, it was found that DEFAULT_GENERATE_FEATURE was used as the benchmark parsing feature switch when there was no defaultFeatures input parameter as the benchmark parsing feature switch. Here is the instantiation code for json.default_generate_feature:

static {
        int features = 0;
        features |= SerializerFeature.QuoteFieldNames.getMask();
        features |= SerializerFeature.SkipTransientField.getMask();
        features |= SerializerFeature.WriteEnumUsingName.getMask();
        features |= SerializerFeature.SortField.getMask();

        DEFAULT_GENERATE_FEATURE = features;

        config(IOUtils.DEFAULT_PROPERTIES);
    }
Copy the code

Fastjson also reads configuration from environment variables to modify DEFAULT_GENERATE_FEATURE(although this is rarely done), but it’s best to verify the actual parsing feature switch in your environment by actually running the program.

@Test public void printFastJsonDefaultGenerateFeature() { for (SerializerFeature feature : SerializerFeature.values()) { if (SerializerFeature.isEnabled(JSON.DEFAULT_GENERATE_FEATURE, feature)) { System.out.println(feature); }}}Copy the code

Fastjson and Jackson serialization feature comparison table

Serialization fastjson and the characteristics of Jackson TestCase SerializationUseJacksonReplaceFastJsonTest. Java

Annotation

Fastjsonzhu’s annotations are not as detailed as Jackson’s annotations, so a Fastjson annotation might be equivalent to a combination of Jackson annotations.

@JSONPOJOBuilder

Specifies the build method used to create Java objects when deserializing, corresponding to Jackson’s @jsonPojoBuilder.

@JSONCreator

Specifies the constructor used to create Java objects when deserializing, corresponding to Jackson’s @jsonCreator.

@JSONField

Specifies the behavior when serializing and deserializing fields. When deserializing, this is equivalent to @jsonProperty + @jsondeserialize + @jsonunwrapped + @jsonFormat + @jsonAlias;

When serialized, this is equivalent to @jsonProperty + @jsonSerialize + @jsonunwrapped + @jsonFormat + @jsonrawValue + @jsonView.

@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.FIELD, Elementtype.parameter}) public @interface JSONField {// Serialize and deserialize the field order, Equivalent to Jackson @jsonProperty.index () int ordinal() default 0; // Field name mapping for serialization and deserialization, equivalent to Jackson @jsonProperty.value () String name() default ""; // Serialization and deserialization of the data format(date format, hexadecimal, etc.), equivalent to Jackson @jsonformat.shape () + @jsonformat.pattern () String format() default ""; // Whether the field is serialized, equivalent to Jackson @jsonProperty.access () Boolean serialize() default true; // Whether to deserialize fields, equivalent to Jackson @jsonProperty.access () Boolean deserialize() default true; // Serialization feature, equivalent to Jackson @jsonProperty.with () SerializerFeature[] serialzeFeatures() default {}; // Deserialization Feature, equivalent to Jackson @jsonformat. with() Feature[] parseFeatures() default {}; // Exclude or include attributes for serialization, equivalent to Jackson's @jsonView String label() default ""; @jsonRawValue Boolean jsonDirect() default false; // Specify the Serializer Class to use for serialization, equivalent to Jackson's @jsonserialize Class<? > serializeUsing() default Void.class; // Specify the Deserializer Class to use for deserialization, equivalent to Jackson's @jsondeserialize Class<? > deserializeUsing() default Void.class; // Specify the field alias to use when deserializing, equivalent to Jackson's @jsonAlias String[] alternateNames() default {}; // Map the field's child attributes to the parent node, equivalent to Jackson's @jsonunwrapped Boolean unwrapped() default false; / / specified serialization field to null to use the default value, equivalent to Jackson's @ JsonProperty. DefaultValue () String defaultValue (default) ""; }Copy the code

Unwrapped usage can reference AnnotationUseJacksonReplaceFastJsonTest. TestJSONFieldUnwrapped in Java.

@JSONType

Specifies the behavior when serializing and deserializing a Java Bean.

@retention (retentionPolicy.runtime) @target ({elementtype.type}) public @interface JSONType { Jackson Has no corresponding feature. Boolean ASM () Default true; // Field sort for serialization and deserialization, equivalent to Jackson's @jsonpropertyOrder.value () String[] Orders () default {}; // Field included in serialization and deserialization, equivalent to Jackson's String[] includes() default {}; // Ignores fields when serializing and deserializing, equivalent to Jackson's @jsonignoreproperties String[] ignores() default {}; // Serialization feature, equivalent to Jackson @jsonProperty.with () SerializerFeature[] serialzeFeatures() default {}; // Deserialization Feature, equivalent to Jackson @jsonformat. with() Feature[] parseFeatures() default {}; / / when the serialization is sorted in alphabetical order according to the field, equivalent to Jackson's @ JsonPropertyOrder. Alphabetic () Boolean alphabetic (default true); // When deserializing polymorphic types, the default subclass is used if the correct subclass cannot be found by other typeName methods, etc. Equivalent to Jackson's @jsonTypeInfo.defaultimpl () Class<?> mappingTo() default Void. // Specify the Java Bean Builder Class when deserializing (which must be the @jsonPojoBuilder annotated Class), equivalent to Jackson's @jsonDeserialize.Builder () Class<? > builder() default Void.class; // Declare an alias for this type, used when deserializing polymorphic types, equivalent to Jackson's @jsonTypename String typeName() default ""; When deserializing a subclass of an interface or abstract class or superclass, specify which field value is equal to the typeName of the subclass to determine the implementation class. Use () = id.custom + @jsonTypeInfo.property () String typeKey() default ""; // Specify the subclass type that can be deserialized when deserializing a subclass of an interface or abstract Class or superclass, equivalent to Jackson's @jsonSubtypes Class<? >[] seeAlso() default{}; // Specify the Serializer Class to use for serialization, equivalent to Jackson's @jsonserialize Class<? > serializer() default Void.class; // Specify the Deserializer Class to use for deserialization, equivalent to Jackson's @jsondeserialize Class<? > deserializer() default Void.class; // For serialization, if filed is an enumeration type, the field of the enumeration is printed as a normal Java bean, instead of the usual Enum. Name () value. Jackson has no corresponding feature Boolean serializeEnumAsJavaBean() Default false; // Specify the field name mapping strategy between JSON and Java beans, Equivalent to Jackson's @ JsonNaming PropertyNamingStrategy naming () default PropertyNamingStrategy. CamelCase; // Specify the Serialize filter to use, equivalent to Jackson's @jsonFilter Class<? extends SerializeFilter>[] serialzeFilters() default {}; }Copy the code

JSONObject & JSONArray

JSONObject and JSONArray in Fastjon

public class JSONObject extends JSON implements Map<String, Object>, Cloneable, Serializable, InvocationHandler { private final Map<String, Object> map; . } public class JSONArray extends JSON implements List<Object>, Cloneable, RandomAccess, Serializable { private static final long serialVersionUID = 1L; private final List<Object> list; protected transient Object relatedArray; protected transient Type componentType; . }Copy the code

JSONObject is a Map

, and JSONArray is a List

. So you can change the JSONObject type to Map

and the JSONArray type to List
. This approach, however, results in a lot of changes to the upper-level API because of the lack of many of the convenient conversion methods provided by JSONObject and JSONArray. If you want to keep JSONObject and JSONArray for the time being, you can take a trick.
,>

,>

keepJSONObject & JSONArrayThe transition method #

Jackson’s official provides org. Json library data type support Jackson – datatype – json – org, therefore can be com. Alibaba. Fastjson. JSONObject replacement for org. Json. JSONObject, Com. Alibaba. Fastjson. JSONArray replacement for org. Json. JSONArray, both the object of the class library API is roughly same, of course, some small changes or avoid. Jackson-datatype -json-org and jackson-datatype-json-lib can also be used to implement Jackson’s own binder for fastJSON data types.

Larva-zhang /jackson-datatype-fastjson welcome to use or mention issues.

Github.com/larva-zhang…

JSONPath#

Json-path /JsonPath is an easy replacement for Fastjson’s JsonPath and is more powerful than FastJSON.

See JsonProvider SPI to use JacksonJsonProvider instead of jSON-path /JsonPath default JsonSmartJsonProvider.

Custom extension

Custom Deserializer

The deserialze method that implements custom Deserializer in Fastjson is usually the deserialze method that implements ObjectDeserializer interface

<T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName);
Copy the code

In Jackson, the way to implement a custom Serializer is to inherit the StdDeserializer abstract class and override the deserialize method

public abstract T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException;
Copy the code

A custom Serializer

The method used to implement custom Serializer in Fastjson is usually the write method that implements the ObjectSerializer interface

void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException;
Copy the code

The way to implement a custom Serializer in Jackson is usually to inherit the StdSerializer abstract class and override the serialize method

public abstract void serialize(T value, JsonGenerator gen, SerializerProvider serializers) throws IOException;
Copy the code

Customize Serialize Filter

Provides six SerializeFilter fastjson, see fastjson/wiki/SerializeFilter. In Jackson it is recommended to inherit SimpleBeanPropertyFilter.

Github.com/alibaba/fas…

Reference documentation

  • alibaba/fastjson
  • FasterXML/jackson
  • Fast Fastjson replacement by Jackson
  • Fastjson the Features that
  • Fastjson SerializerFeatures instructions
  • Fastjson JSONField instructions
  • Jackson — Decide What Fields Get Serialized/Deserialized