+1

Sử dụng Json schema để validate dữ liệu Json (phần 1)

Khi bạn đang làm việc với các dữ liệu phức tạp và cấu trúc, bạn cần phải xác định xem dữ liệu là hợp lệ hay không. JSON-Schema là tiêu chuẩn của tài liệu JSON mô tả cấu trúc và các yêu cầu của dữ liệu JSON của bạn. Trong loạt bài này, bạn sẽ học cách sử dụng JSON-Schema để xác nhận dữ liệu.

Phần một này, tôi muốn giới thiệu cho các bạn cách nhìn tổng quan về việc sử dụng Json Schema và những lợi thế nó mang lại, đồng thời cũng đề cập đến Propertiesdata type là hai thành phần cơ bản và quan trọng trong Json-Schema.

Hãy nói rằng bạn có một cơ sở dữ liệu của người sử dụng mà mỗi bản ghi trông tương tự như ví dụ này:

{
  "id": 64209690,
  "name": "Jane Smith",
  "email": "jane.smith@gmail.com",
  "phone": "07777 888 999",
  "address": {
    "street": "Flat 1, 188 High Street Kensington",
    "postcode": "W8 5AA",
    "city": "London",
    "country": "United Kingdom"
  },
  "personal": {
    "DOB": "1982-08-16",
    "age": 33,
    "gender": "female"
  },
  "connections": [
    {
      "id": "35434004285760",
      "name": "John Doe",
      "connType": "friend",
      "since": "2014-03-25"
    },
    {
      "id": 13418315,
      "name": "James Smith",
      "connType": "relative",
      "relation": "husband",
      "since": "2012-07-03"
    }
  ],
  "feeds": {
    "news": true,
    "sport": true,
    "fashion": false
  },
  "createdAt": "2015-09-22T10:30:06.000Z"
}

Câu hỏi chúng ta sẽ là làm thế nào để xác định xem các bản ghi như trên là hợp lệ hay không. Ví dụ là rất hữu ích nhưng chưa đủ khi mô tả các yêu cầu dữ liệu của bạn. JSON-Schema đến để giải quyết. Đây là một trong các lược đồ có thể mô tả một hồ sơ người dùng:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "id": "http://mynet.com/schemas/user.json#",
  "title": "User",
  "description": "User profile with connections",
  "type": "object",
  "properties": {
    "id": {
      "description": "positive integer or string of digits",
      "type": ["string", "integer"],
      "pattern": "^[1-9][0-9]*$",
      "minimum": 1
    },
    "name": { "type": "string", "maxLength": 128 },
    "email": { "type": "string", "format": "email" },
    "phone": { "type": "string", "pattern": "^[0-9()\\-\\.\\s]+$" },
    "address": {
      "type": "object",
      "additionalProperties": { "type": "string" },
      "maxProperties": 6,
      "required": ["street", "postcode", "city", "country"]
    },
    "personal": {
      "type": "object",
      "properties": {
        "DOB": { "type": "string", "format": "date" },
        "age": { "type": "integer", "minimum": 13 },
        "gender": { "enum": ["female", "male"] }
      }
      "required": ["DOB", "age"],
      "additionalProperties": false
    },
    "connections": {
      "type": "array",
      "maxItems": 150,
      "items": {
        "title": "Connection",
        "description": "User connection schema",
        "type": "object",
        "properties": {
          "id": {
            "type": ["string", "integer"],
            "pattern": "^[1-9][0-9]*$",
            "minimum": 1
          },
          "name": { "type": "string", "maxLength": 128 },
          "since": { "type": "string", "format": "date" },
          "connType": { "type": "string" },
          "relation": {},
          "close": {}
        },
        "oneOf": [
          {
            "properties": {
              "connType": { "enum": ["relative"] },
              "relation": { "type": "string" }
            },
            "dependencies": {
              "relation": ["close"]
            }
          },
          {
            "properties": {
              "connType": { "enum": ["friend", "colleague", "other"] },
              "relation": { "not": {} },
              "close": { "not": {} }
            }
          }
        ],
        "required": ["id", "name", "since", "connType"],
        "additionalProperties": false
      }
    },
    "feeds": {
      "title": "feeds",
      "description": "Feeds user subscribes to",
      "type": "object",
      "patternProperties": {
        "^[A-Za-z]+$": { "type": "boolean" }
      },
      "additionalProperties": false
    },
    "createdAt": { "type": "string", "format": "date-time" }
  }
}

Có một cái nhìn tại các sơ đồ trên và hồ sơ người dùng nó mô tả (đó là hợp lệ theo lược đồ này). Có rất nhiều giải thích để làm ở đây. Mã JavaScript để xác nhận hồ sơ người một cách giản lược có thể là:

var Ajv = require('ajv');
var ajv = Ajv({allErrors: true});
var valid = ajv.validate(userSchema, userData);
if (valid) {
  console.log('User data is valid');
} else {
  console.log('User data is INVALID!');
  console.log(ajv.errors);
}

Hoặc với hiệu suất tốt hơn có thể là:

var validate = ajv.compile(userSchema);
var valid = validate(userData);
if (!valid) console.log(validate.errors);

Trước khi chúng ta tiếp tục, chúng ta hãy nhanh chóng đến với với tất cả các nguyên nhân tại sao.

Tại sao chia validate data thành các bước

  • không thất bại một cách nhanh chóng
  • tránh dữ liệu trùng lặp
  • để đơn giản code
  • để sử dụng validation code trong các tests.

Tại sao JSON (và không XML)?

  • Được xem như một mở rộng XML
  • Dễ dàng hơn để xử lý và ngắn gọn hơn so với XML
  • Chiếm ưu thế phát triển web vì JavaScript

Tại sao sử dụng Schema?

  • Dễ dàng trong việc khai báo.
  • Dễ dàng hơn để duy trì(maintain)
  • Có thể được hiểu bởi non-coders
  • Không cần viết code, sử dụng open-source

Tại sao JSON-Schema?

  • việc áp dụng rộng rãi nhất trong số tất cả các tiêu chuẩn cho JSON validation
  • Phát triển liên tục trưởng thành (phiên bản hiện tại là 4, có những đề xuất cho phiên bản 5)
  • Bao gồm một phần lớn các validation JSON
  • Dễ phân tích cú pháp cho schemas
  • Nền tảng độc lập
  • xác nhận 30 + dễ dàng mở rộng cho nhau ngôn ngữ, bao gồm cả 10+ cho Javascript, vì vậy không cần phải mã đó cho mình

Data properties

  • Bởi vì hầu hết Json data tồn tại với nhiều thuộc tính, vậy nên thuộc tính "properties" được sử dụng một cách rất phổ biến. Nó chỉ được áp dụng với objects

  • Bạn phải chú ý rằng ở trong ví dụ trên, với mỗi thuộc tính trong "properties" keyword, miêu tả một thuộc tính dựa trên dữ liệu data của bạn.

  • Mỗi giá trị của mỗi thuộc tính bản thân nó là Json-Schema, Json-Schema là cấu trúc đệ quy tiêu chuẩn.

  • Điều quan trọng ở đây là "properties" keyword không tạo ra bất kỳ thuộc tính required , nó chỉ định nghĩa mô hình cho các thuộc tính được thể hiện trong dữ liệu.

Ví dụ

{
  "properties": {
    "foo": { "type": "string" }
  }
}

# data valid
{foo: "bar"}, {foo: "bar", baz: 1}, {baz: 1}, {}
# invalid data
{ foo: 1 }

Data Type

Bạn đã tiếp xúc với keyword type đồng nghĩa với gì chưa? Nó có thể là từ khóa quan trọng nhất. Các giá của của nó(một xâu ký tự, một mảng các xâu ký tự), định nghĩa kiểu (hoặc các kiểu) của dữ liệu sẽ khả dụng.

Giống như bạn đã nhìn ở trên trong ví dụ, dữ liệu của user phải là một object

Hầu hết các keyword được sử dụng một cách tự nhiên trong data types, như trong ví dụ. Từ khóa "properties" chỉ được áp dụng cho từ khioas objects và từ khóa "pattern" chỉ được áp dụng cho "strings".

Ví dụ

# Trong schema
{ "pattern": "^[0-9]+$" }
# thì dữ liệu khả dụng
"12345"

Tuy nhiên như nói ở trên, pattern chỉ áp dụng với kiểu dữ liệu là : "string" vì vậy schema đúng sẽ là :

{
  "type": "string",
  "pattern": "^[0-9]+$"
}

Bạn có thể tạo ra một schema linh hoạt hơn bằng cách sử dụng nhiều kiểu dữ liệu. Giống như thuộc tính "id" trong ví dụ ban đầu, nó có thể viết thành:

{
  "type": ["string", "integer"],
  "pattern": "^[1-9][0-9]*$",
  "minimum": 1
}

Schema yêu cầu rằng dữ liệu có thể là kiểu "string" hoặc "integer". ỏ đó có từ khóa "pattern" chỉ áp dụng cho dữ liệu kiểu string. Nó yêu cầu rằng string nên tồn tại là các số bắt đầu từ 0, từ khóa "minimum" chỉ áp dụng với kiểu là số (integer, number...), nó yêu cầu giá trị nhỏ nhất của số là 1. Một cách khác, chúng ta có thể viết schema như sau(với cùng một yêu cầu như trên):

{
  "anyOf": [
    { "type": "string", "pattern": "^[1-9][0-9]*$" },
    { "type": "integer", "minimum": 1 },
  ]
}

Nó chỉ là các con đường khác nhau để định nghĩa JSON-Schema, tuy nhiên, cách viết thứ hai có vẻ ngắn và nhanh hơn để validate dữ liệu trong hầu hết các trường hợp.

Các kiểu dữ liệu có thể sử dụng trong các schema là: "object", "array", "number", "integer", "string", "boolean" và "null", kiểu number bao gồm "integer và các kiểu số

Phần tiếp theo chúng ta sẽ tìm hiểu về việc validate các kiểu dữ liệu (như integer, string, array...) một cách chi tiết và rõ ràng hơn, sẽ có những ví dụ cụ thể và đưa ra những tùy chọn để validate những kiểu dữ liệu này.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí