In addition to FastJson, you have a choice: Gson Easy Guide

Foreword

This weekend is several technology bloggers with a public article number fastjson vulnerability has been found that this service can lead to paralysis harm! Scraper, not long before the event from vulnerabilities, fastjson appeared serious Bug. At present the project to make a lot of use fastjson convert an object with JSON data, and the need to re-deploy the updated version, can be said to be time-consuming. At the same time, it gives me a new way of thinking, the face of a large number of powerful open source libraries, we can not blindly introduced into the project, many open source frameworks a destabilizing factor is enough to make a project suffered disaster. Taking advantage of the weekend, under study at home with the same JSON object conversion of outstanding open source framework Gson, and intends to use in future projects fastjson place gradually replaced by the use of Gson, summary record of the content under study, hoping to small partners also help.

All involved in this paper are in the following code snippet repository, interested in learning junior partner welcome reference:

https://github.com/wrcj12138aaa/gson-actions

Supported in:

  • JDK 8
  • Gson 2.8.5
  • JUnit 5.5.1
  • Lomo 1.18.8

About Gson

Prior to the formal introduction Gson, we can start with the official wiki look Gson description, to understand what it is?

Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object。

As can be seen from the description, Gson Java library for Java objects and JSON format conversion of the data string. At first it is widely used on the Android platform and Java server within Google. After the 2008 open-source, open-source framework has become Google has a widely used, until now (2019.09.08) has been 1W6 multi-satellite, as well as Spring Framework Class Library integrated Jackson the same effect, as well as open source on GitHub Ali the fastjson and so on.

In characteristics, Gson provides a simple API fromJson/toJsonto achieve conversion between Java and JSON, and can generate a compact, readable output JSON string, conversion and complex object supports custom represents rich enough to meet the daily development Most of our JSON data processing needs.

We generally convert between the object and the call JSON string serialization and deserialization (Serialization / Deserialization). Converted to JSON string object is called serialization, to convert the object into JSON string is called deserialization.

Gson basic use

Use Gson framework sequences and Anti operation sequence, are inseparable from com.google.gson.Gsonthe object, it is also critical objects Gson frame, providing a common API includes a variety of sequences and deserialization mode.

Gson create objects There are two main ways:

  • Using new keywords directly created:Gson gson = new Gson()
  • Built by GsonBuilder objects:Gson gson = new GsonBuilder().create()

Typically, Gson objects created above in two ways during the serialization and de-serialization operation when the behavior is the same, but the second way when building Gson objects, allowing for additional custom behavior, such as JSON formatted string the outputting content, whether the sequence of null values and the like.

Java serialization

Simple object serialization

We can by the above two different serialized Java objects look at the effect of the following examples:

public class ResultTest {
    @Test
    void test_serialization() {
        Gson gson = new Gson();
        Result result = new Result(200, "成功", null);
        String json = gson.toJson(result);
        System.out.println("json is " + json);
      
        Gson buildedGson = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
        String buildedJson = buildedGson.toJson(result);
        System.out.println("buildedJson is " + buildedJson);
    }

    class Result {
        private int code;
        private String message;
        private Object data;

        public Result(int code, String message, Object data) {
            this.code = code;
            this.message = message;
            this.data = data;
        }
    }
}

Run the test case in the console following log output:

From the results, the default object behavior Gson serialized objects will nullfield value is ignored, the com.google.gson.GsonBuilder#serializeNullsapproach would allow Gson object serialization nullfield; JSON string and the normal sequence of the compact format to save memory string, use com.google.gson.GsonBuilder#setPrettyPrintingJSON string after the final output of the method is more readable format. Of course, in addition to these two methods, GsonBuilder API also offers many custom serialization and de-serialization behavior, we will further explain the contents of the back.

JosnObject generate JSON

In addition to the object to be converted into a custom class manner than JSON may also be used to build the framework JsonObject Gson general object, and then use the toJsonmethod of generating JSON string, added in the original test under the test class class, and run to see the effect of the following

@Test
void test_jsonObject_serialization() {
  Gson gson = new Gson();
  JsonObject jsonObject = new JsonObject();
  jsonObject.addProperty("code", 400);
  jsonObject.addProperty("message", "参数错误");
  String toJson = gson.toJson(jsonObject);
  String exceptedJson = "{\"code\":400,\"message\":\"参数错误\"}";
  Assertions.assertEquals(exceptedJson, toJson); //true
}

JsonObject using the addProperty(property,value)method can only be used to add a String, Number, Boolean, Character these four categories of data, because the interior is invoked com.google.gson.JsonObject#add, the value packaged in a JsonPrimitive object, and then saved to internal self-defined LinkedTreeMapset of variable members; if the object needs to JsonObject when the add other objects, you need to directly add(String property, JsonElement value)add a JsonElement object method. JsonElement here is an abstract class, JsonObject and JsonPrimitive inherit JsonElement, so we ended up by another JsonObject object as an attribute of an object on the original JsonObject:

Gson gson = new Gson();
JsonObject jsonObject = new JsonObject();
//...
JsonObject nestJsonObject = new JsonObject();
nestJsonObject.addProperty("username", "one");
nestJsonObject.addProperty("score", 99);
jsonObject.add("data", nestJsonObject);
String toJson2 = gson.toJson(jsonObject);
System.out.println(toJson2);
// {"code":400,"message":"参数错误","data":{"username":"one","score":99}}

JSON deserialization

Deserialize simple objects

Now we look at the JSON deserialized into Java objects usage, where the main use is com.google.gson.Gson#fromJsonits most basic use is to fromJson(String json, Class<T> classOfT)try a JSON string into the specified Class object, if the conversion fails, will throw JsonSyntaxExceptionan exception. We can add a test case in the original code, run facie effect:

@Test
void test_deserialization() {
    String json = "{\"code\":400,\"message\":\"参数错误\"}";
    Result result = new Gson().fromJson(json, Result.class);
    Assertions.assertEquals(400, result.code); // true
    Assertions.assertEquals("参数错误", result.message); // true
}

Deserialization Map

In addition to Java objects serialized as JSON string custom outside, we can also turn Map collection, Gson provides a set of conversion of the Map, is also very simple to use:

@Test
void test_map() {
    String jsonString = "{'employee.name':'one','employee.salary':10}";
    Gson gson = new Gson();
    Map map = gson.fromJson(jsonString, Map.class);
    assertEquals(2, map.size());
        assertEquals("one", map.get("employee.name"));
    assertEquals(Double.class, map.get("employee.name").getClass());
}

Note that after conversion Map object type is not true that we often use HashMap, but Gson custom collection LinkedTreeMapthat implements the Map interface to store key-value pairs, implemented on the new and delete optimized, and key-value pairs stored in the order as the order of traversal, which is placed with the first to be traversed. In addition, JSON string of numerical data are in turn converted Double type, and the true/falsedata is converted to a Boolean type is, specific reference may be determined based on com.google.gson.internal.bind.ObjectTypeAdapter#readimplementation of the method.

JSON and Array, List Conversion

Array JSON conversion

When we are converted to JSON data array, in a similar manner to convert normal object, toJsonthe method directly into JSON data fromJsonconversion specified array type to a corresponding type of array.

@Test
void test_array() {
  Gson gson = new Gson();
  int[] ints = {1, 2, 3, 4, 5};
  String[] strings = {"abc", "def", "ghi"};
  String s = gson.toJson(ints);// [1,2,3,4,5]
  assertEquals("[1,2,3,4,5]", s); // true

  String s1 = gson.toJson(strings);// ["abc", "def", "ghi"]
  assertEquals("[\"abc\",\"def\",\"ghi\"]", s1);
  String[] strings1 = gson.fromJson(s1, String[].class);
  assertEquals(strings.length, strings1.length); // true
  assertEquals(strings[0], strings1[0]); // true

  int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class);
  assertEquals(1, ints2[0]); // true
  assertEquals(5, ints2[4]); // true
}

List JSON conversion

To List JSON data into data, using the processing Gson manner as Array data; here is mainly about the List object data into JSON slightly different operation, to a JSON array data into a custom class when the List, we follow the original wording as follows:

@Test
public void givenJsonString_whenIncorrectDeserializing() {
    Gson gson = new Gson();
    String inputString = "[{\"id\":1,\"name\":\"one\"},{\"id\":2,\"name\":\"two\"}]";
    List<Person> outputList = gson.fromJson(inputString, List.class);
        outputList.get(0).getId();
}

But unfortunately, after running this code will throw ClassCastExceptionan exception, described as follows:

java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.one.learn.Person
...

From the above description, we can know executed fromJsonafter, after deserialization get the List element type LinkedTreeMap, instead of Person, so access will be thrown when the id attribute to the Person object way ClassCastExceptionabnormal. Then how to deal with it, we need to call another Gson of fromJsonmethods: fromJson(String json, Type typeOfT), look under use

@Test
public void givenJsonString_whenCorrectDeserializing_() {
  Gson gson = new Gson();
  String inputString = "[{\"id\":1,\"name\":\"one\"},{\"id\":2,\"name\":\"two\"}]";
  Type type = new TypeToken<List<Person>>(){}.getType();
  List<Person> outputList = gson.fromJson(inputString, type);
  int id = outputList.get(0).getId();
  assertEquals(1, id); // true
  assertEquals("one", outputList.get(0).getName()); // true
}

This method TypeToken Type object by object getTypeacquired method, is a generic type associated TypeToken objects. And here is TypeToken Gson introduced to support generic class, to solve the problem can not provide Java generic type represented due TypeToken construction method is protectedmodified, can not be constructed directly, we need to use the written new TypeToken<List<String>>() {}.getType()form.

Gson Advanced Usage

After contact with the basic use of Gson, we went on to further study other uses of Gson.

Generic object deserialization

Section on simple contact with Gson support for generics, then use the code to demonstrate under its powerful place, first of all we will accept the generic parameter adjustment under Result class above:

class Result<T> {
  private int code;
  private String message;
  private T data;

  public Result(int code, String message, T data) {
    this.code = code;
    this.message = message;
    this.data = data;
  }
}

Then a JSON string embedded object is parsed into Result <User> objects, the following sample code:

@Test
void test_genric_object() {
  String json = "{\"code\":200,\"message\":\"操作成功\",\"data\":{\"username\": \"one\",\"avater\": \"image.jpg\"" +
    "}}";
  Type type = new TypeToken<Result<User>>(){}.getType();
  Result<User> result = new Gson().fromJson(json, type);
  Assertions.assertEquals(200, result.code);
  Assertions.assertEquals("one", result.data.getUsername());
  Assertions.assertEquals("image.jpg", result.data.getAvater());
}

class User {
  private String username;
  private String avater;

  public String getUsername() {
    return username;
  }

  public String getAvater() {
    return avater;
  }
}

DETAILED TypeToken object acquisition using the generic type Result <User>, then fromJsonthe incoming deserialized will operate in accordance with a corresponding type of execution method.

Custom Serialization

If we want certain fields of Java objects special treatment, such as the sequence of some of the hidden field, the field of data formatting, etc. We can, serialization logic custom implementation JsonSerializer interface. For example, we need to deal with specific attributes Date type format, the class can be declared DateSerializer implemented as follows:

class DateSerializer implements JsonSerializer<Date> {
    SimpleDateFormat dateTime = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(dateTime.format(src));
    }
}

Then Gson objects before building, the use GsonBuilder register DateSerializer instance, used as follows:

Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new DateSerializer()).create();

Thus, when a sequence of the event for a field of type Date, are customized by serializea method in date yyyy-MM-ddformat output side of the sample code below:

@Test
void test_dateSerializer() {
  MyObject myObject = new MyObject(new Date(), "one");
  Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new DateSerializer()).create();
  String json = gson.toJson(myObject);
  String exceptedJson = "{\"date\":\"2019-09-08\",\"name\":\"one\"}";
  Assertions.assertEquals(exceptedJson, json); // true
}

class MyObject {
    private Date date;
    private String name;

    public MyObject(Date date, String name) {
        this.date = date;
        this.name = name;
    }

    public MyObject() {
    }
}

Custom deserialization

And custom serialization to achieve a similar fashion, you want to customize the deserialization logic, it is necessary to implement the same interface called JsonDeserializer carried out anti-implement custom serialization logic. Example, there are a JSON string content {"CODE": 400, "MESSAGE": "参数错误"}, needs to be Result object into the previously mentioned anti-sequence, since the field names are not the same, in order to achieve the corresponding converter, it is necessary to customize ResultDeserializer class, specifically implemented as follows:

class ResultDeserializer implements JsonDeserializer<Result> {
    @Override
    public Result deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        JsonObject object = json.getAsJsonObject();
        Result<Object> result = new Result<>(object.getAsJsonPrimitive("CODE").getAsInt(),object.getAsJsonPrimitive("MESSAGE").getAsString(), null);
        return result;
    }
}

The next example is the use of GsonBuilder register ResultDeserializer, Gson generate a corresponding object and deserialization:

@Test
void test_resultDeserializer() {
    //language=JSON
        String json = "{\"CODE\": 400,\"MESSAGE\": \"参数错误\"}";
    Gson gson = new GsonBuilder().registerTypeAdapter(Result.class, new ResultDeserializer())
            .create();
    Result result = gson.fromJson(json, Result.class);
    Assertions.assertEquals(400, result.code); // true
    Assertions.assertEquals("参数错误", result.message); // true
}

Gson common comment

Gson In addition to providing some of the API for developers to use, there are a number of notes have properties that can be used, then it introduces the most popular in the Gson comment.

@Expose

This comment can only be used on the field, the role is to indicate whether the corresponding fields will be serialized or deserialized when exposed, have two properties serializeand deserializedefault are true. When a field to add comments @Expose(serialize = true, deserialize = false), it means that this field is to make visible when deserialization ignores the assignment at the time of serialization. Require additional note is that, @ Expose comment only when building Gson with GsonBuilder way limited, and must be called before building excludeFieldsWithoutExposeAnnotationmethod, the following are specific examples of their use:


@Test
void test_expose() {
    MySubClass subclass = new MySubClass(42L, "the answer", "Verbose field not to serialize");
    MyClass source = new MyClass(1L, "foo", "bar", subclass);
    Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
    String s = gson.toJson(source);
    System.out.println(s);
        // {"id":1,"name":"foo","subclass":{"id":42,"description":"the answer","otherVerboseInfo":"Verbose field not to serialize"}}
}

@Data
@AllArgsConstructor
class MyClass {
    private long id;
    @Expose(serialize = false, deserialize = true)
    private String name;
    private transient String other;
    @Expose
    private MySubClass subclass;
}

@Data
@AllArgsConstructor
class MySubClass {
    @Expose
    private long id;
    @Expose
    private String description;
    @Expose
    private String otherVerboseInfo;
}

In Gson in transientthe keyword field default will not be modified serialization and de-serialization, this behavior is consistent with serialization and de-serialization of Java native operation.

@Since

The annotation for marking the corresponding field type or version, the version number can be specified so Gson serialization and deserialization. When there are multiple versions of the JSON data fields on Web services corresponding class, this annotation will be very helpful.

Similarly, the annotation used only for the GsonBuilderembodiment constructed Gson objects, using setVersionthe method specified valid version, only the parsed version of the corresponding field object, the following is a specific example:

public class VersioningSupportTest {
    @Test
    void test() {
        VersionedClass versionedObject = new VersionedClass();
        Gson gson = new GsonBuilder().setVersion(1.0).create();
        String jsonOutput = gson.toJson(versionedObject);
        System.out.println(jsonOutput); // {"newField":"new","field":"old"}
    }
}

class VersionedClass {
    @Since(1.1)
    private final String newerField;
    @Since(1.0)
    private final String newField;
    private final String field;

    public VersionedClass() {
        this.newerField = "newer";
        this.newField = "new";
        this.field = "old";
    }
}

@SerializedName

This annotation is simple to use, is also useful. @SerializedName specifies the name of a member of the following fields are serialized and deserialized used is the specific use:

public class JSONFieldNamingSupportTest {
    private class SomeObject {
        @SerializedName("custom_naming")
        private final String someField;
        private final String someOtherField;

        public SomeObject(String a, String b) {
            this.someField = a;
            this.someOtherField = b;
        }
    }

    @Test
    void test() {
        SomeObject someObject = new SomeObject("first", "second");
        String jsonRepresentation = gson.toJson(someObject);
        System.out.println(jsonRepresentation);
        // {"custom_naming":"first","someOtherField":"second"}
        SomeObject someObject1 = gson.fromJson(jsonRepresentation, SomeObject.class);
        System.out.println(someObject1);
        // SomeObject{someField='first', someOtherField='second'}
    }
}

@JsonAdapter

Unlike the above comments, @JsonAdapteronly acting on the class, the main role is instead of GsonBuilder.registerTypeAdapterperforming the method, by directly @JsonAdapter(aClass.class)specify JsonDeserializer object or objects JsonSerializer embodiment, the same functions can be thought of, and priority than GsonBuilder.registerTypeAdaptera higher priority, since only the registerTypeAdapterexecution would be simplified annotation methods, there is no longer presentation, the text immediately preceding custom deserialization using the Result of a class can see the effect.

Epilogue

This paper summarizes the study serialization and de-serialization operations use Gson framework, and introduces a variety of features Gson usage, hope to deal with JSON data headache for small partners help.

Recommended Reading

Reference material

  • https://github.com/google/gson/blob/master/UserGuide.md
  • https://www.jianshu.com/p/e740196225a4
  • https://juejin.im/post/5aad29f8518825558453c6c9
  • https://www.baeldung.com/gson-deserialization-guide
  • https://www.baeldung.com/gson-string-to-jsonobject

Guess you like

Origin www.cnblogs.com/one12138/p/11494256.html