Giảm kích thước dữ liệu JSON

1. Giới thiệu

Các ứng dụng Java thường sử dụng JSON như một định dạng chung để gửi và nhận dữ liệu. Hơn nữa, nó được sử dụng như một giao thức tuần tự hóa để lưu trữ dữ liệu. Với kích thước dữ liệu JSON nhỏ hơn, các ứng dụng của chúng tôi trở nên rẻ hơn và nhanh hơn.

Trong hướng dẫn này, chúng ta sẽ xem xét các cách khác nhau để giảm kích thước của JSON trong các ứng dụng Java của chúng tôi.

2. Mô hình miền và dữ liệu thử nghiệm

Hãy tạo mô hình miền cho Khách hàng với một số dữ liệu liên hệ:

public class Customer { private long id; private String firstName; private String lastName; private String street; private String postalCode; private String city; private String state; private String phoneNumber; private String email;

Lưu ý rằng tất cả các trường sẽ là bắt buộc, ngoại trừ phoneNumberemail .

Để kiểm tra chính xác sự khác biệt về kích thước dữ liệu JSON, chúng tôi cần ít nhất vài trăm trường hợp Khách hàng . Họ phải có dữ liệu khác nhau để làm cho các thử nghiệm của chúng tôi giống như thật hơn. Mockaroo trang web tạo dữ liệu giúp chúng tôi ở đây. Chúng tôi có thể tạo 1.000 bản ghi dữ liệu JSON ở đó miễn phí, ở định dạng của riêng chúng tôi và với dữ liệu thử nghiệm xác thực.

Hãy định cấu hình mockaroo cho mô hình miền của chúng tôi:

Sau đây, một số mục cần ghi nhớ:

  • Đây là nơi chúng tôi chỉ định tên trường
  • Ở đây, chúng tôi đã chọn các kiểu dữ liệu của các trường của chúng tôi
  • 50% số điện thoại trống trong dữ liệu giả
  • 30% địa chỉ email cũng trống

Tất cả các ví dụ mã bên dưới sử dụng cùng một dữ liệu của 1.000 khách hàng từ mockaroo . Chúng tôi sử dụng phương thức gốc Customer.fromMockFile () để đọc tệp đó và biến nó thành các đối tượng Khách hàng .

Chúng tôi sẽ sử dụng Jackson làm thư viện xử lý JSON của chúng tôi.

3. Kích thước dữ liệu JSON với Tùy chọn mặc định của Jackson

Hãy ghi một đối tượng Java vào JSON với các tùy chọn Jackson mặc định:

Customer[] customers = Customer.fromMockFile(); ObjectMapper mapper = new ObjectMapper(); byte[] feedback = mapper.writeValueAsBytes(customers); 

Hãy xem dữ liệu giả cho Khách hàng đầu tiên :

{ "id" : 1, "firstName" : "Horatius", "lastName" : "Strognell", "street" : "4848 New Castle Point", "postalCode" : "33432", "city" : "Boca Raton", "state" : "FL", "phoneNumber" : "561-824-9105", "email" : "[email protected]" }

Khi sử dụng các tùy chọn Jackon mặc định, mảng byte dữ liệu JSON với tất cả 1.000 khách hàng có kích thước 181,0 KB .

4. Nén bằng gzip

Là dữ liệu văn bản, dữ liệu JSON nén độc đáo. Đó là lý do tại sao gzip là tùy chọn đầu tiên của chúng tôi để giảm kích thước dữ liệu JSON. Hơn nữa, nó có thể được tự động áp dụng trong HTTP, giao thức phổ biến để gửi và nhận JSON.

Hãy lấy JSON được tạo với các tùy chọn Jackson mặc định và nén nó bằng gzip . Điều này dẫn đến 45,9 KB, chỉ bằng 25,3% so với kích thước ban đầu . Vì vậy, nếu chúng tôi có thể bật tính năng nén gzip thông qua cấu hình, chúng tôi sẽ cắt giảm 75% kích thước dữ liệu JSON mà không có bất kỳ thay đổi nào đối với mã Java của chúng tôi!

Nếu ứng dụng Spring Boot của chúng tôi cung cấp dữ liệu JSON cho các dịch vụ hoặc giao diện người dùng khác, thì chúng tôi sẽ bật tính năng nén gzip trong cấu hình Spring Boot. Hãy xem một cấu hình nén điển hình trong cú pháp YAML:

server: compression: enabled: true mime-types: text/html,text/plain,text/css,application/javascript,application/json min-response-size: 1024 

Đầu tiên, chúng tôi đã bật tính năng nén nói chung bằng cách cài đặt được bật là true. Sau đó, chúng tôi đặc biệt bật tính năng nén dữ liệu JSON bằng cách thêm ứng dụng / json vào danh sách các loại kịch câm . Cuối cùng, hãy lưu ý rằng chúng tôi đặt kích thước phản hồi tối thiểu dài 1,024 byte. Điều này là do nếu chúng tôi nén lượng dữ liệu ngắn, chúng tôi có thể tạo ra dữ liệu lớn hơn so với dữ liệu gốc.

Thông thường, các proxy như NGINX hoặc máy chủ web như Máy chủ Apache HTTP sẽ phân phối dữ liệu JSON đến các dịch vụ hoặc giao diện người dùng khác. Định cấu hình nén dữ liệu JSON trong các công cụ này nằm ngoài phạm vi của hướng dẫn này.

Một hướng dẫn trước đây về gzip cho chúng ta biết rằng gzip có nhiều cấp độ nén khác nhau. Các ví dụ mã của chúng tôi sử dụng gzip với mức nén Java mặc định. Spring Boot, proxy hoặc máy chủ web có thể nhận được các kết quả nén khác nhau cho cùng một dữ liệu JSON.

Nếu chúng tôi sử dụng JSON làm giao thức tuần tự hóa để lưu trữ dữ liệu, chúng tôi sẽ cần tự nén và giải nén dữ liệu.

5. Tên trường ngắn hơn trong JSON

Cách tốt nhất là sử dụng tên trường không quá ngắn cũng không quá dài. Hãy bỏ qua điều này vì lợi ích của việc minh họa: Chúng tôi sẽ sử dụng tên trường một ký tự trong JSON, nhưng chúng tôi sẽ không thay đổi tên trường Java. Điều này làm giảm kích thước dữ liệu JSON nhưng làm giảm khả năng đọc JSON. Vì nó cũng sẽ yêu cầu cập nhật cho tất cả các dịch vụ và giao diện người dùng, chúng tôi có thể sẽ chỉ sử dụng các tên trường ngắn này khi lưu trữ dữ liệu:

{ "i" : 1, "f" : "Horatius", "l" : "Strognell", "s" : "4848 New Castle Point", "p" : "33432", "c" : "Boca Raton", "a" : "FL", "o" : "561-824-9105", "e" : "[email protected]" }

Thật dễ dàng để thay đổi tên trường JSON với Jackson trong khi vẫn giữ nguyên tên trường Java. Chúng tôi sẽ sử dụng chú thích @JsonProperty :

@JsonProperty("p") private String postalCode; 

Sử dụng tên trường gồm một ký tự dẫn đến dữ liệu có kích thước bằng 72,5% kích thước ban đầu. Hơn nữa, sử dụng gzip sẽ nén nó xuống còn 23,8%. Con số này không nhỏ hơn nhiều so với 25,3% mà chúng tôi nhận được từ việc nén dữ liệu gốc bằng gzip . Chúng tôi luôn cần tìm kiếm mối quan hệ chi phí - lợi ích phù hợp. Việc mất khả năng đọc với một kích thước nhỏ sẽ không được khuyến khích cho hầu hết các trường hợp.

6. Nối tiếp với một mảng

Hãy xem cách chúng ta có thể giảm kích thước dữ liệu JSON hơn nữa bằng cách loại bỏ hoàn toàn các tên trường. Chúng tôi có thể đạt được điều này bằng cách lưu trữ mảng khách hàng trong JSON của chúng tôi. Lưu ý rằng chúng tôi cũng sẽ giảm khả năng đọc. Và chúng tôi cũng cần cập nhật tất cả các dịch vụ và giao diện người dùng sử dụng dữ liệu JSON của chúng tôi:

[ 1, "Horatius", "Strognell", "4848 New Castle Point", "33432", "Boca Raton", "FL", "561-824-9105", "[email protected]" ] 

Việc lưu trữ Khách hàng dưới dạng một mảng dẫn đến kết quả đầu ra là 53,1% so với kích thước ban đầu và 22,0% với nén gzip . Đây là kết quả tốt nhất của chúng tôi cho đến nay. Tuy nhiên, 22% không nhỏ hơn đáng kể so với 25,3% mà chúng tôi nhận được khi chỉ nén dữ liệu gốc bằng gzip .

Để tuần tự hóa một khách hàng dưới dạng một mảng, chúng ta cần có toàn quyền kiểm soát việc tuần tự hóa JSON. Tham khảo lại hướng dẫn Jackson của chúng tôi để có thêm ví dụ.

7. Loại trừ Giá trị rỗng

Jackson and other JSON processing libraries may not handle JSON null values correctly when reading or writing JSON. For example, Jackson writes a JSON null value by default when it encounters a Java null value. That's why it's a good practice to remove empty fields in JSON data. This leaves the initialization of empty values to each JSON processing library and reduces the JSON data size.

In our mock data, we set 50% of the phone numbers, and 30% of the email addresses, as empty. Leaving out these null values reduces our JSON data size to 166.8kB or 92.1% of the original data size. Then, gzip compression will drop it to 24.9%.

Now, if we combine ignoring null values with the shorter field names from the previous section, then we'll get more significant savings: 68.3% of the original size and 23.4% with gzip.

We can configure the omission of null value fields in Jackson per class or globally for all classes.

8. New Domain Class

We achieved the smallest JSON data size so far by serializing it to an array. One way of reducing that even further is a new domain model with fewer fields. But why would we do that?

Let's imagine a front-end for our JSON data that shows all customers as a table with two columns: name and street address. Let's write JSON data specifically for this front-end:

{ "id" : 1, "name" : "Horatius Strognell", "address" : "4848 New Castle Point, Boca Raton FL 33432" }

Notice how we concatenated the name fields into name and the address fields into address. Also, we left out email and phoneNumber.

This should produce much smaller JSON data. It also saves the front-end from concatenating the Customer fields. But on the downside, this couples our back-end tightly to the front-end.

Let's create a new domain class CustomerSlim for this front-end:

public class CustomerSlim { private long id; private String name; private String address;

If we convert our test data to this new CustomerSlim domain class, we‘ll reduce it to 46.1% of the original size. That will be using the default Jackson settings. If we use gzip it goes down to 15.1%. This last result is already a significant gain over the previous best result of 22.0%.

Next, if we also use one-character field names, this gets us down to 40.7% of the original size, with gzip further reducing this to 14.7%. This result is only a small gain of over 15.1% we reached with the Jackson default settings.

No fields in CustomerSlim are optional, so leaving out empty values has no effect on the JSON data size.

Tối ưu hóa cuối cùng của chúng tôi là tuần tự hóa một mảng. Bằng cách tuần tự CustomerSlim thành một mảng, chúng tôi đạt được kết quả tốt nhất: 34,2% so với kích thước ban đầu và 14,2% với gzip . Vì vậy, ngay cả khi không nén, chúng tôi cũng loại bỏ gần 2/3 dữ liệu gốc. Và quá trình nén sẽ thu nhỏ dữ liệu JSON của chúng tôi xuống chỉ còn một phần bảy kích thước ban đầu!

9. Kết luận

Trong bài viết này, trước tiên chúng ta đã biết lý do tại sao chúng ta cần giảm kích thước dữ liệu JSON. Tiếp theo, chúng tôi đã tìm hiểu nhiều cách khác nhau để giảm kích thước dữ liệu JSON này. Cuối cùng, chúng tôi đã học cách giảm hơn nữa kích thước dữ liệu JSON với mô hình miền tùy chỉnh cho một giao diện người dùng.

Mã hoàn chỉnh luôn có sẵn trên GitHub.