openplanning

Phân tích JSON trong TypeScript với thư viện json2typescript

  1. json2typescript
  2. Cài đặt
  3. Ví dụ cơ bản
  4. @JsonProperty
  5. @JsonConverter

1. json2typescript

json2typescript là một thư viện chứa các lớp trợ giúp (helper classes) để định nghĩa ánh xạ giữa một văn bản JSON đến một đối tượng TypeScript. Thư viện này được lấy cảm hứng từ Java Jackson và có tính năng tương tự.
Về cơ bản, json2typescript thích hợp cho các dự án lớn, đòi hỏi một vài bước cấu hình. Nếu bạn chỉ làm việc với JSON trong một dự án TypeScript nhỏ, hãy xem xét sử dụng các phương thức JSON.parse(..)JSON.stringify(..), đơn giản và không cần bất kỳ một cấu hình bổ xung nào:

2. Cài đặt

Đầu tiên, cài đặt thư viện json2typescript vào dự án:
npm install json2typescript
Tiếp theo, thêm các property sau vào file tsconfig.json:
tsconfig.json
{
  "compilerOptions": {  
    ...
    "experimentalDecorators": true,
    "allowJs": true
  }
}

3. Ví dụ cơ bản

Trong ví dụ này, chúng ta sẽ tạo 2 file model:
  • model_file1.ts
  • model_file2.ts.
Các lớp được định nghĩa trong các file model. Sử dụng @JsonObject@JsonProperty để chú thích (annotate) trên lớp và property của lớp nhằm gợi ý cho công cụ cách chuyển đổi đối tượng TypeScript thành văn bản JSON và ngược lại.
@JsonObject
Một lớp được chú thích (annotate) với @JsonObject thì đối tượng của nó có thể chuyển đổi thành một văn bản JSON và ngược lại.
@JsonProperty
Một property của đối tượng TypeScript có thể chuyển đổi thành một property của JSON nếu nó được chú thích (annotate) bởi @JsonProperty. Nếu không, nó sẽ bị bỏ qua.
Tất cả các property của lớp TypeScript được chú thích bởi @JsonProperty phải được gán giá trị mặc định, nếu không, kết quả có thể không như mong đợi của bạn.
model_file1.ts
import {
    JsonObject, JsonProperty, JsonConvert, OperationMode,
    ValueCheckingMode, PropertyConvertingMode
} from "json2typescript";

@JsonObject("Contact")
export class Contact {
    // PropertyConvertingMode.MAP_NULLABLE is default.
    // MAP_NULLABLE: Throw Exception if JSON value is undefined
    // IGNORE_NULLABLE: Using Java property default value if JSON value is undefined
    // PASS_NULLABLE: Set Java property value to null if JSON value is undefined
    @JsonProperty("address", String, PropertyConvertingMode.MAP_NULLABLE)
    address: string = '';
    @JsonProperty("phone", String, PropertyConvertingMode.MAP_NULLABLE)
    phone: string = '';
}
@JsonObject("Employee")
export class Employee {
    @JsonProperty("employeeName", String, PropertyConvertingMode.MAP_NULLABLE)
    name: string = '';
    @JsonProperty("email", String, PropertyConvertingMode.MAP_NULLABLE)
    email: string = '';
    @JsonProperty("contact", Contact, PropertyConvertingMode.PASS_NULLABLE)
    contact?: Contact = new Contact();
}
PropertyConvertingMode.MAP_NULLABLE:
Đây là chế độ chuyển đổi mặc định. Một ngoại lệ sẽ được ném ra nếu không tồn tại property nào của JSON tương ứng với property này của lớp TypeScript.
PropertyConvertingMode.IGNORE_NULLABLE:
Property của đối tượng TypeScript sẽ nhận giá trị mặc định nếu không có property tương ứng trong JSON.
PropertyConvertingMode.PASS_NULLABLE:
Property của đối tượng TypeScript sẽ nhận giá trị null nếu không có property tương ứng trong JSON.
model_file2.ts
import {
    JsonObject, JsonProperty, JsonConvert, OperationMode,
    ValueCheckingMode, PropertyConvertingMode
} from "json2typescript";

import { Contact } from './model_file1';

@JsonObject("Company")
class Company {
    @JsonProperty("companyName", String, PropertyConvertingMode.MAP_NULLABLE)
    name: string = '';
    @JsonProperty("contact", Contact, PropertyConvertingMode.PASS_NULLABLE)
    contact?: Contact = new Contact();
}
Nếu bạn sử dụng Visual Studio Code để viết code có thể bạn nhận được một thông báo lỗi như dưới đây, hãy xem cách khắc phục lỗi được cộng đồng chia sẻ trên stackoverflow:
Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.ts(1219)
json2typescript_ex1.ts
import {
    JsonObject, JsonConvert, OperationMode, ValueCheckingMode
} from "json2typescript";

import { Employee, Contact } from './model_file1';

// @see Employee class in model_file1.ts
let jsonStr = `  {
    "employeeName": "John Smith",
    "email": "john@example.com",
    "contact": {
       "address": "Address 1",
       "phone": "12345"
    }
} `;
let jsonConvert: JsonConvert = new JsonConvert();

// ----------- JSON Text to TypeScript Object ------------------  
let jsonObj: any = JSON.parse(jsonStr);  
let emp: Employee = jsonConvert.deserializeObject(jsonObj, Employee);
console.log('JSON to TypeScript:');
console.log(emp);
console.log(' ------------- ');

// ----------- TypeScript Object to JSON Text ------------------
let jsonText = jsonConvert.serialize(emp);
console.log('TypeScript to JSON:');
console.log(jsonText);
Output:
JSON to TypeScript:
Employee {
  name: 'John Smith',
  email: 'john@example.com',
  contact: Contact { address: 'Address 1', phone: '12345' }
}
 -------------
TypeScript to JSON:
{
  employeeName: 'John Smith',
  email: 'john@example.com',
  contact: { address: 'Address 1', phone: '12345' }
}
json2typescript_ex2.ts
import {
    JsonObject, JsonConvert, OperationMode, ValueCheckingMode
} from "json2typescript";

import { Employee, Contact } from './model_file1';

// @see Employee class in model_file1.ts
let jsonStr = `  {
   "employeeName": "John Smith",
    "email": "john@example.com"
} `;
let jsonConvert: JsonConvert = new JsonConvert();

// ----------- JSON Text to TypeScript Object ------------------
let jsonObj: any = JSON.parse(jsonStr);  
let emp: Employee = jsonConvert.deserializeObject(jsonObj, Employee);
console.log('JSON to TypeScript:');
console.log(emp);
console.log(' ------------- ');

// ----------- TypeScript Object to JSON Text ------------------
let jsonText = jsonConvert.serialize(emp);
console.log('TypeScript to JSON:');
console.log(jsonText);
Output:
JSON to TypeScript:
Employee {
  name: 'John Smith',
  email: 'john@example.com',
  contact: undefined
}
-------------
TypeScript to JSON:
{
  employeeName: 'John Smith',
  email: 'john@example.com',
  contact: undefined
}

4. @JsonProperty

Một property của đối tượng TypeScript có thể chuyển đổi thành một property của JSON nếu nó được chú thích (annotate) bởi @JsonProperty. Nếu không, nó sẽ bị bỏ qua.
Tất cả các property của lớp TypeScript được chú thích bởi @JsonProperty phải được gán giá trị mặc định, nếu không, kết quả có thể không như mong đợi của bạn.
Ví dụ:
// Model class:
@JsonObject("User")
export class User {
    @JsonProperty("userName", String, PropertyConvertingMode.MAP_NULLABLE)
    userName: string = '';
    @JsonProperty("joinDate", DateConverter)
    joinDate: Date = new Date();
}
Cú pháp:
@JsonProperty("JSON_Property_Name", JSON_Type, Custom_Converter, PropertyConvertingMode)
JSON_Property_Name
Tên của một property trong JSON.
JSON_Type (Optional)
Một kiểu dữ liệu JSON.
  • String, [String]
  • Number, [Number]
  • Boolean, [Boolean]
  • Any, [Any]
Custom_Converter (Optional)
Xem ví dụ tại mục @JsonConverter.
PropertyConvertingMode (Optional)
PropertyConvertingMode.MAP_NULLABLE:
  • Đây là chế độ chuyển đổi mặc định. Một ngoại lệ sẽ được ném ra nếu không tồn tại property nào của JSON tương ứng với property này của lớp TypeScript.
PropertyConvertingMode.IGNORE_NULLABLE:
  • Property của đối tượng TypeScript sẽ nhận giá trị mặc định nếu không có property tương ứng trong JSON.
PropertyConvertingMode.PASS_NULLABLE:
  • Property của đối tượng TypeScript sẽ nhận giá trị null nếu không có property tương ứng trong JSON.

5. @JsonConverter

Trong một số trường hợp, bạn có thể cần thực hiện chuyển đổi tùy chỉnh giữa các đối tượng JSON và các đối tượng TypeScript. Bạn có thể định nghĩa các bộ chuyển đổi tùy chỉnh giống như thế này:
model_file3.ts
import {
    JsonObject, JsonProperty, JsonConvert, OperationMode,
    ValueCheckingMode, PropertyConvertingMode, JsonConverter, JsonCustomConvert
} from "json2typescript";
// Custom Converter:
@JsonConverter
class DateConverter implements JsonCustomConvert<Date> {
    serialize(date: Date): any {
        return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
    }
    deserialize(date: any): Date {
        return new Date(date);
    }
}
// Model class:
@JsonObject("User")
export class User {
    @JsonProperty("userName", String, PropertyConvertingMode.MAP_NULLABLE)
    userName: string = '';
    @JsonProperty("joinDate", DateConverter)
    joinDate: Date = new Date();
}
json2typescript_ex3.ts
import {
    JsonObject, JsonConvert, OperationMode, ValueCheckingMode
} from "json2typescript";

import { User  } from './model_file3';

// @see User class in model_file3.ts
let jsonStr = `  {
   "userName": "tom",
    "joinDate": "2021-12-15"
} `;
let jsonConvert: JsonConvert = new JsonConvert();

// ----------- JSON Text to TypeScript Object ------------------
let jsonObj: any = JSON.parse(jsonStr);  
let user: User = jsonConvert.deserializeObject(jsonObj, User);
console.log('JSON to TypeScript:');
console.log(user);
console.log(' ------------- ');

// ----------- TypeScript Object to JSON Text ------------------
let jsonText = jsonConvert.serialize(user);
console.log('TypeScript to JSON:');
console.log(jsonText);
Output:
JSON to TypeScript:
User { userName: 'tom', joinDate: 2021-12-15T00:00:00.000Z }
 -------------
TypeScript to JSON:
{ userName: 'tom', joinDate: '2021-12-15' }