Json is a text-based data exchange format that is much lighter than XML. Json parsing and generation of a lot of ways, in the Android platform on the most commonly used class library Gson and FastJson two, here is to introduce Gson

Gson’s GitHub home page is here: Gson

One, the basic usage of Gson

1.1. Gson object

Before you serialize or dissequence, you need to instantiate a com.google.gson. gson object. There are two ways to get the gson object

Gson = new Gson(); Gson = new Gson(); Gson Gson = new GsonBuilder().create(); Gson Gson = new GsonBuilder().create();Copy the code

1.2. Generate Json

Gson makes it easy to generate Json strings by using the four overloaded methods of addProperty

public static void main(String[] args) { JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("String", "leavesC"); jsonObject.addProperty("Number_Integer", 23); JsonObject. AddProperty (" Number_Double ", 22.9); jsonObject.addProperty("Boolean", true); jsonObject.addProperty("Char", 'c'); System.out.println(); System.out.println(jsonObject); }Copy the code

The addProperty method calls the add(String Property, JsonElement value) method, which converts the basic data type into a JsonElement object, which is an abstract class. JsonObject inherits from JsonElement, so we can build a JsonElement from JsonObject itself

public static void main(String[] args) { JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("String", "leavesC"); jsonObject.addProperty("Number", 23); JsonObject. AddProperty (" Number ", 22.9); jsonObject.addProperty("Boolean", true); jsonObject.addProperty("Char", 'c'); JsonObject jsonElement = new JsonObject(); jsonElement.addProperty("Boolean", false); JsonElement. AddProperty (" Double ", 25.9); jsonElement.addProperty("Char", 'c'); jsonObject.add("JsonElement", jsonElement); System.out.println(); System.out.println(jsonObject); }Copy the code

1.3. Json and array, List conversion

Json array and string array

Public static void main(String[] args) {Gson Gson = new Gson(); String jsonArray = "[\"https://github.com/leavesC\",\"https://www.jianshu.com/u/9df45b87cfdf\",\"Java\",\"Kotlin\",\"Git\",\"GitHub\"]"; String[] strings = gson.fromJson(jsonArray, String[].class); System.out.println(" convert Json array to string array: "); for (String string : strings) { System.out.println(string); JsonArray = gson.tojson (jsonArray, new TypeToken<String>() {}.getType()); System.out.println("\n string array to Json array: "); System.out.println(jsonArray); }Copy the code

Json array and List

Public static void main(String[] args) {// List Gson Gson = new Gson(); String jsonArray = "[\"https://github.com/leavesC\",\"https://www.jianshu.com/u/9df45b87cfdf\",\"Java\",\"Kotlin\",\"Git\",\"GitHub\"]"; List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() { }.getType()); System.out.println("\nJson array to List: "); for (String string : stringList) { System.out.println(string); JsonArray = gson.tojson (stringList, new TypeToken<List<String>>() {}.getType()); System.out.println("\nList to Json array: "); System.out.println(jsonArray); }Copy the code

1.4 serialization and deserialization

Gson also provides toJson() and fromJson() methods to transform Model and Json. The former implements serialization, and the latter implements deserialization. First, declare a User class

/ * * * the author: chenZY * time: 2018/3/17 oh * description: https://github.com/leavesC * / public class User {private String name; private int age; private boolean sex; public User(String name, int age, boolean sex) { this.name = name; this.age = age; this.sex = sex; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", sex=" + sex + '}'; }}Copy the code

The method of serialization is simple: call the toJson method on the gson object, passing in the object to be serialized

Public static void main(String[] args) {// serialize User User = new User("leavesC", 24, true); Gson gson = new Gson(); System.out.println(); System.out.println(gson.toJson(user)); }Copy the code

The reverse ordering is similar

Public static void main(String[] args) {// deserialize String userJson = "{\"name\":\"leavesC\",\"age\":24,\"sex\":true}"; Gson gson = new Gson(); User user = gson.fromJson(userJson, User.class); System.out.println(); System.out.println(user); }Copy the code

Attribute renaming

Continuing with the User class declared in the previous section, based on the attribute names declared by the User class, the mobile developer expects the interface to return data in the following format

{"name":"leavesC","age":24,"sex":true}
Copy the code

If there is no communication with the server or if the API is revised, the format of the data returned by the interface may look like this

{"Name":"leavesC","age":24,"sex":true}
Copy the code
{"userName":"leavesC","age":24,"sex":true}
Copy the code

If you continue with the methods described in the previous section, you will undoubtedly parse the wrong example

Public static void main(String[] args) {// deserialize String userJson = "{\"userName\":\"leavesC\",\"age\":24,\"sex\":true}"; Gson gson = new Gson(); User user = gson.fromJson(userJson, User.class); System.out.println(); System.out.println(user); }Copy the code

The value of the name attribute cannot be resolved, so it is null

In this case, to accommodate multiple formats of data, you need to use the SerializedName annotation. According to the SerializedName declaration, SerializedName contains two property values, a string and an array of strings, and the array of strings has default values

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface SerializedName {
    String value();

    String[] alternate() default {};
}
Copy the code

The purpose of SerializedName is to instruct Gson on how to associate the original property name with other special case property names during serialization or deserialization

For example, modify the User class to declare a SerializedName annotation for name with a value of userName

/** * https://github.com/leavesC */ public class User { @SerializedName("userName") private String name; private int age; private boolean sex; }Copy the code

The Json format changes when the sequence is in place

Public static void main(String[] args) {// serialize User User = new User("leavesC", 24, true); Gson gson = new Gson(); System.out.println(); System.out.println(gson.toJson(user)); }Copy the code

The same goes for deserialization, which resolves to the correct property value

Public static void main(String[] args) {// deserialize String userJson = "{\"userName\":\"leavesC\",\"age\":24,\"sex\":true}"; Gson gson = new Gson(); User user = gson.fromJson(userJson, User.class); System.out.println(); System.out.println(user); }Copy the code

One more question remains: should we declare multiple User classes in order to deal with inconsistent attribute names? This is obviously not realistic, so you also need to set multiple alternate property names for the User class, which requires alternate property values in the SerializedName annotation.

/** * https://github.com/leavesC */ public class User { @SerializedName(value = "userName", alternate = {"user_name", "Name"}) private String name; private int age; private boolean sex; }Copy the code

The following situations can be deserialized correctly

Public static void main(String[] args) {// deserialize Gson Gson = new Gson(); String userJson = "{\"userName\":\"leavesC\",\"age\":24,\"sex\":true}"; User user = gson.fromJson(userJson, User.class); System.out.println(); System.out.println(user); userJson = "{\"user_name\":\"leavesC\",\"age\":24,\"sex\":true}"; user = gson.fromJson(userJson, User.class); System.out.println(); System.out.println(user); userJson = "{\"Name\":\"leavesC\",\"age\":24,\"sex\":true}"; user = gson.fromJson(userJson, User.class); System.out.println(); System.out.println(user); }Copy the code

3. Field filtering

Sometimes not all fields need to be serialized and deserialized, so certain fields need to be excluded. There are four ways to achieve this requirement.

3.1. Based on the @Expose annotation

The Expose annotation contains two property values, and both have default values declared. Expose is used to Expose fields. Serialize is used to specify whether to serialize and deserialize is used to specify whether to deserialize. If the field does not declare the Expose annotation, it means that no serialization and deserialization are performed, which is equivalent to both property values being false. In addition, the Expose annotation needs to be used with Gson objects built by GsonBuilder to be effective.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Expose {
    boolean serialize() default true;

    boolean deserialize() default true;
}

Copy the code

There are four ways to declare an annotation value for the Expose annotation

Expose(serialize = false, deserialize = true) @expose (serialize = false, deserialize = true) Expose(serialize = true, deserialize = false) Expose(serialize = false, deserialize = false) // Both serialization and deserialization do not take effectCopy the code

Now for an example, modify the User class

/** * https://github.com/leavesC */ public class User { @Expose(serialize = true, Deserialize = true) private String a; @expose (serialize = false, deserialize = true) private String b; @expose (serialize = true, deserialize = false) private String c; @expose (serialize = false, deserialize = false) private String d; private String e; public User(String a, String b, String c, String d, String e) { this.a = a; this.b = b; this.c = c; this.d = d; this.e = e; } @Override public String toString() { return "User{" + "a='" + a + '\'' + ", b='" + b + '\'' + ", c='" + c + '\'' + ", d='" + d + '\'' + ", e='" + e + '\'' + '}'; }}Copy the code

As indicated above, only fields with the Expose annotation and the serialize value to true will be serialized, and only fields with the Expose annotation and the deserialize value to true will be deserialized

    public static void main(String[] args) {
        Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
        User user = new User("A", "B", "C", "D", "E");
        System.out.println();
        System.out.println(gson.toJson(user));

        String json = "{\"a\":\"A\",\"b\":\"B\",\"c\":\"C\",\"d\":\"D\",\"e\":\"E\"}";
        user = gson.fromJson(json, User.class);
        System.out.println();
        System.out.println(user.toString());
    }
Copy the code

3.2. Version based

Gson provides two @since and @until annotations to filter fields based on version. Both @Since and @until contain a Double attribute value that sets the version number. A. Since B. Since C. Since A. Until B. Until C. Until D. Until So far “, also to be used with GsonBuilder.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Since {
    double value();
}

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Until {
    double value();
}
Copy the code

Fields are serialized and deserialized when the version (set by GsonBuilder) is greater than or equal to the value of the Since property or less than the value of the Until property. Fields without declared annotations are serialized and dissequentialized

Now for an example, modify the User class

/** * https://github.com/leavesC * / public class User {@ Since (1.4) private String a; @ Since (1.6) private String b; @ Since (1.8) private String c; @ Until (1.6) private String d; @ Until (2.0) private String e; public User(String a, String b, String c, String d, String e) { this.a = a; this.b = b; this.c = c; this.d = d; this.e = e; } @Override public String toString() { return "User{" + "a='" + a + '\'' + ", b='" + b + '\'' + ", c='" + c + '\'' + ", d='" + d + '\'' + ", e='" + e + '\'' + '}'; }}Copy the code
Public static void main(String[] args) {Gson Gson = new GsonBuilder().setVersion(1.6).create(); User user = new User("A", "B", "C", "D", "E"); System.out.println(); System.out.println(gson.toJson(user)); String json = "{\"a\":\"A\",\"b\":\"B\",\"c\":\"C\",\"d\":\"D\",\"e\":\"E\"}"; user = gson.fromJson(json, User.class); System.out.println(); System.out.println(user.toString()); }Copy the code

3.3. Based on access modifiers

Access Modifier by Java. Lang. Reflect. Provide the definition of type int Modifier, object and GsonBuilder excludeFieldsWithModifiers method takes an int type variable parameter, Take an example of an access modifier field that specifies no serialization or deserialization

/** * https://github.com/leavesC */ public class ModifierSample { public String publicField = "public"; protected String protectedField = "protected"; private String privateField = "private"; String defaultField = "default"; final String finalField = "final"; static String staticField = "static"; }Copy the code
    public static void main(String[] args) {
        Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.PRIVATE, Modifier.STATIC).create();
        ModifierSample modifierSample = new ModifierSample();
        System.out.println(gson.toJson(modifierSample));
    }
Copy the code

3.4. Policy-based

The GsonBuilder class contains setExclusionStrategies(ExclusionStrategy… Strategies) method is used to pass in parameters of variable length. It is used to directly exclude the specified field name or the specified field type

/** * https://github.com/leavesC */ public class Strategies { private String stringField; private int intField; private double doubleField; public Strategies(String stringField, int intField, double doubleField) { this.stringField = stringField; this.intField = intField; this.doubleField = doubleField; } @Override public String toString() { return "Strategies{" + "stringField='" + stringField + '\'' + ", intField=" + intField + ", doubleField=" + doubleField + '}'; }}Copy the code
public static void main(String[] args) { Gson gson = new GsonBuilder().setExclusionStrategies(new ExclusionStrategy() { @override public Boolean shouldSkipField(FieldAttributes FieldAttributes) {return fieldAttributes.getName().equals("intField"); } @Override public boolean shouldSkipClass(Class<? > aClass) {return aclass.getName ().equals(double.class.getName()); } }).create(); Strategies Strategies = new Strategies("stringField", 111, 11.22); System.out.println(); System.out.println(gson.toJson(strategies)); String json = "{\" stringField \ ": \" stringField \ ", \ "intField \" : 111, \ "doubleField \" : 11.22} "; strategies = gson.fromJson(json, Strategies.class); System.out.println(); System.out.println(strategies); }Copy the code

Fields with field name “intField” and field type double will be excluded

The setExclusionStrategies method works for both serialization and deserialization, but if you want to specify an exclusion policy for either case or for each, you can use the following two methods instead

addSerializationExclusionStrategy(ExclusionStrategy strategy);

addDeserializationExclusionStrategy(ExclusionStrategy strategy);
Copy the code

Iv. Personalized configuration

4.1 output NULL

For Gson, if a property value is null at serialization time, the field will not participate in serialization time. If you want to display the output of the field, you can configure it through GsonBuilder

/** * https://github.com/leavesC */ public class Strategies { private String stringField; private int intField; private double doubleField; }Copy the code
Public static void main(String[] args) {Gson Gson = new GsonBuilder().serializenulls () // Output null.create(); Strategies Strategies = new Strategies(null, 24, 22.333); System.out.println(); System.out.println(gson.toJson(strategies)); }Copy the code

4.2 Format output Json

The default serialized Josn string is less intuitive, and you can format the output

Public static void main(String[] args) {Gson Gson = new GsonBuilder().serializenulls () // Output null .setPrettyPrinting()// Formats the output.create(); Strategies Strategies = new Strategies(null, 24, 22.333); System.out.println(); System.out.println(gson.toJson(strategies)); }Copy the code

4.3 Formatting time

Gson can also format time values

/ * * * the author: chenZY * time: 2018/3/17 oh * description: https://github.com/leavesC * / public class Strategies {private Date Date; private Date date2; public Strategies(Date date, Date date2) { this.date = date; this.date2 = date2; } @Override public String toString() { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS", Locale.CHINA); return "Strategies{" + "date=" + simpleDateFormat.format(date) + ", date2=" + simpleDateFormat.format(date2) + '}'; }}Copy the code
Public static void main(String[] args) {Gson Gson = new GsonBuilder().setPrettyPrinting()// Format the output .setdateFormat (" YYYY-mm-dd HH: MM :ss:SSS")// Format the time.create (); Date date = new Date(); Strategies strategies = new Strategies(date, new Date(date.getTime() + 1000000)); System.out.println(); System.out.println(gson.toJson(strategies)); String json = "{\n" + " \"date\": \"2018-03-17 19:38:50:033\",\n" + " \"date2\": \"2018-03-17 19:55:30:033\"\n" + "}"; System.out.println(); System.out.println(gson.fromJson(json, Strategies.class)); }Copy the code

Fifth, TypeAdapter

A TypeAdapter is a generic abstract class used to take over certain types of serialization and deserialization processes, including two abstract methods for custom serialization and deserialization processes

public abstract void write(JsonWriter var1, T var2) throws IOException;

public abstract T read(JsonReader var1) throws IOException;
Copy the code

Let’s do a simple example

/ * * * the author: chenZY * time: 2018/3/17 oh * description: https://github.com/leavesC * / public class User {private String name; private int age; private boolean sex; public User() { } public User(String name, int age, boolean sex) { this.name = name; this.age = age; this.sex = sex; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", sex=" + sex + '}'; }}Copy the code

We’re going to define a subclass of TypeAdapter called UserTypeAdapter to take over the serialization and deserialization of the User class and we’re going to set the Key in the Json to start with a capital letter when the User class is serialized, Deserialization supports “name” and “name” Json styles

public class UserTypeAdapter extends TypeAdapter<User> { @Override public void write(JsonWriter jsonWriter, User User) throws IOException {// Stream serialization into objects jsonWriter.beginObject(); Jsonwriter.name (" name ").value(user.getName()); jsonWriter.name("Age").value(user.getAge()); jsonWriter.name("Sex").value(user.isSex()); // End the stream serialization jsonwriter.endobject (); } @Override public User read(JsonReader jsonReader) throws IOException { User user = new User(); // Stream deserialization starts jsonreader.beginObject (); While (jsonreader.hasnext ()) {switch (jsonreader.nextname ()) {// case "name": case "name": case "name": user.setName(jsonReader.nextString()); break; case "age": user.setAge(jsonReader.nextInt()); break; case "sex": user.setSex(jsonReader.nextBoolean()); break; }} // Stream deserialization ends jsonReader.endobject (); return user; }}Copy the code
    public static void main(String[] args) {
        Gson gson = new GsonBuilder().registerTypeAdapter(User.class, new UserTypeAdapter()).create();
        User user = new User("leavesC", 24, true);
        System.out.println();
        System.out.println(gson.toJson(user));

        String json = "{\"Name\":\"leavesC\",\"age\":24,\"sex\":true}";
        user = gson.fromJson(json, User.class);
        System.out.println();
        System.out.println(user);
    }
Copy the code

You can see that the User class is serialized and deserialized according to the predefined policies

JsonSerializer and JsonDeserializer

The TypeAdapter takes over both serialization and antiserialization, but Gson also provides an interface that only takes over the serialization process, JsonSerializer, for example

    public static void main(String[] args) {
        Gson gson = new GsonBuilder().registerTypeAdapter(User.class, new JsonSerializer<User>() {
            @Override
            public JsonElement serialize(User user, Type type, JsonSerializationContext jsonSerializationContext) {
                JsonObject jsonObject = new JsonObject();
                jsonObject.addProperty("NameHi", user.getName());
                jsonObject.addProperty("Sex", user.isSex());
                jsonObject.addProperty("Age", user.getAge());
                return jsonObject;
            }
        }).create();
        User user = new User("leavesC", 24, true);
        System.out.println();
        System.out.println(gson.toJson(user));
    }
Copy the code

The JsonDeserializer interface provides a deserialization interface

public static void main(String[] args) { Gson gson = new GsonBuilder().registerTypeAdapter(User.class, new JsonDeserializer<User>() { @Override public User deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { JsonObject jsonObject = jsonElement.getAsJsonObject(); String name = null; If (jsonObject.has("userName")) {name = jsonObject.get("userName").getAsString(); } else if (jsonObject.has("name")) { name = jsonObject.get("name").getAsString(); } int age = jsonObject.get("age").getAsInt(); boolean sex = jsonObject.get("sex").getAsBoolean(); return new User(name, age, sex); } }).create(); String json = "{\"userName\":\"leavesC\",\"sex\":true,\"age\":24}"; User user = gson.fromJson(json, User.class); System.out.println(); System.out.println(user); json = "{\"name\":\"leavesC\",\"sex\":true,\"age\":24}"; user = gson.fromJson(json, User.class); System.out.println(); System.out.println(user); }Copy the code

When using TypeAdapter, JsonSerializer, and JsonDeserializer, the registerTypeAdapter method is always required. Is there an easier way to registerTypeAdapter, JsonSerializer, and JsonDeserializer? Yes, Gosn also provides another annotation @jsonAdapter for simple declarations

Similarly, it is declared that serialization or deserialization of the User class is done by the UserTypeAdapter, and that annotations take precedence over the registerTypeAdapter method

@JsonAdapter(UserTypeAdapter.class)
public class User {

}
Copy the code

Seven, TypeAdapterFactory

TypeAdapterFactory is the factory class used to create TypeAdapter. It takes TypeToken to find the corresponding TypeAdapter. If there is no TypeAdapter, it returns null and is serialized and deserialized by Gson’s default processing method. Otherwise, the TypeAdapter predefined by the user will handle it

Gson gson = new GsonBuilder().registerTypeAdapterFactory(new TypeAdapterFactory() { @Override public <T> TypeAdapter<T> Create (Gson Gson, TypeToken<T> TypeToken) {// If Gson needs TypeAdapter associated with the User class, If (typeToken.getType().getTypename ().equals(user.class.getTypename ())) {return (TypeAdapter<T>) new UserTypeAdapter(); } // Return null if it is not found; } }).create();Copy the code

Eight, epilogue

Does this article seem a little too long? The introduction of Gson knowledge point here is about the same, later if also found new content, I will continue to add, now so

My GitHub: leavesC -> github.com/leavesC