JSON Toolkit

Ranh giới chuyển đổi JSON ↔ YAML và XML

Kết luận: Tuyên bố rằng JSON là tập con của YAML chỉ đảm bảo một chiều, còn XML hoàn toàn không có ánh xạ JSON chính thức — mọi thứ đều dựa trên quy ước.

Trung cấpĐọc khoảng 12 phútXuất bản 2026-10-30Cập nhật 2026-10-30
Ảnh bìa về ranh giới chuyển đổi JSON ↔ YAML và XML

Kết luận: dùng JSON làm định dạng trung gian — phân tích nguồn thành mô hình dữ liệu rồi tuần tự hóa sang đích; trước khi chuyển đổi, hãy quyết định xem comment, thuộc tính XML và namespace, cùng kiểu số hoặc boolean có cần sống sót hay không.

Một pipeline chuyển đổi có dùng được hay không không phụ thuộc vào việc đầu ra trông đẹp đến đâu, mà vào việc bạn có ý thức được nó đã vứt đi bao nhiêu.

1. JSON và YAML: quan hệ chứa một chiều

Đặc tả YAML 1.2 nói thẳng: JSON là tập con của YAML. Nghĩa là mọi JSON hợp lệ đều là YAML hợp lệ:

{"name": "json", "tags": ["a", "b"], "count": 3}

Đoạn này vừa là JSON vừa là YAML flow style. Chiều ngược lại không đúng.

Tính năng JSON YAML 1.2
Comment ✗ ✓
Anchor và alias ✗ ✓
Nhiều tài liệu (---) ✗ ✓
Khóa không phải chuỗi ✗ ✓ (1: a, true: b)
Khóa phức hợp ✗ ✓ (? [a, b])
Nhạy cảm với thụt lề ✗ ✓ (thụt lề bằng tab bị từ chối)
Suy luận kiểu tường minh ngầm định (0o17, 0x1F, .inf)
Khả năng đọc trên một dòng tốt kém (nội dung dài phụ thuộc cú pháp folding)

1.1 Suy luận kiểu là bẫy lớn nhất

YAML phân tích một số giá trị trông như chuỗi thành kiểu khác:

zip: 01234        # string or octal integer? the YAML 1.2 core schema treats it as a string
version: 1.10     # 1.1 (a float); the semantics are lost
country: NO       # a string in YAML 1.2; boolean false in YAML 1.1
date: 2026-10-30  # some implementations parse it as a date object

Ví dụ NO đã làm khổ không ít người: với parser YAML 1.1 (một số thư viện cũ), country: NO trở thành false. Biện pháp: đặt trong ngoặc mọi định danh "trông giống số hoặc boolean", như version: "1.10".

1.2 Anchor, alias và nhiều tài liệu

defaults: &defaults
  retries: 3
  timeout: 30

serviceA:
  <<: *defaults
  host: a.example.com

---
serviceB:
  <<: *defaults
  host: b.example.com

Sau khi mở rộng anchor rồi chuyển sang JSON, bạn thường nhận được hai object cấu hình trùng lặp, hoặc một mảng (nhiều tài liệu phải trở thành [{...}, {...}]). Dù theo cách nào, anchor không được phục hồi khi chuyển ngược — mất mát đó là không thể tránh.

1.3 Chuyển đổi mà vẫn giữ được comment

Hầu hết thư viện chỉ đơn giản bỏ comment khi chuyển đổi. Khi cần giữ chúng, hãy dùng Document API của gói yaml:

import { parseDocument } from 'yaml';

const document = parseDocument(`
# Service configuration
port: 8080
`);

document.set('port', 3000);
console.log(document.toString());
// # Service configuration
// port: 3000

Thử nghiệm rất đơn giản: nếu tệp này sẽ bị con người chỉnh đi chỉnh lại nhiều lần, đừng đi theo con đường "parse → object → serialize".

1.4 Cách kiểm tra nhất quán khứ hồi

/**
 * Verifies round-trip consistency for JSON → YAML → JSON.
 * @param sourceValue Original JSON value
 * @param stringify YAML serialization function
 * @param parse YAML parsing function
 * @returns Whether the two results are exactly equal
 */
function isRoundTripStable(
  sourceValue: unknown,
  stringify: (input: unknown) => string,
  parse: (input: string) => unknown,
): boolean {
  const yamlText = stringify(sourceValue);
  const restored = parse(yamlText);
  return JSON.stringify(restored) === JSON.stringify(sourceValue);
}

Chạy nó trên dữ liệu thật đáng giá hơn đọc tài liệu mười lần. Hãy đảm bảo bộ mẫu của bạn bao phủ: null, chuỗi rỗng, mảng rỗng, "00", "1e5", âm không, số nguyên lớn hơn 2^53, và chuỗi dài có chứa xuống dòng.

2. JSON và XML: không có ánh xạ chuẩn

XML ra đời trước JSON rất lâu và có năng lực cấu trúc mạnh hơn nhiều (thuộc tính, namespace, nội dung hỗn hợp, DTD). Giữa hai định dạng không có đặc tả ánh xạ chính thức; mọi công cụ đều làm cùng một việc: chọn một quy ước, rồi cố gắng không mất quá nhiều.

2.1 Quy ước phổ biến

Quy ước Biểu diễn Ví dụ
Phần tử → khóa object Phần tử cùng tên trở thành một trường <a>1</a> → {"a": "1"}
Thuộc tính → tiền tố @ @name <a id="1"/> → {"a": {"@id": "1"}}
Text → #text Khi có cả thuộc tính và text <a id="1">hi</a> → {"a": {"@id": "1", "#text": "hi"}}
Phần tử lặp → mảng Hai lần xuất hiện trở lên thì thành mảng <i>1</i><i>2</i> → {"i": ["1","2"]}
Namespace → tiền tố ns:tag <ns:tag/> → {"ns:tag": ...}
<order id="A1001">
  <item sku="X1" qty="2">笔记本</item>
  <item sku="X2" qty="1">钢笔</item>
</order>
{
  "order": {
    "@id": "A1001",
    "item": [
      { "@sku": "X1", "@qty": "2", "#text": "笔记本" },
      { "@sku": "X2", "@qty": "1", "#text": "钢笔" }
    ]
  }
}

2.2 Năm mất mát không thể tránh

  1. Thứ tự phần tử: object JSON không có thứ tự, trong khi XML là một chuỗi phần tử có thứ tự;
  2. Thuộc tính so với phần tử con: chúng mang ngữ nghĩa khác nhau trong XML, nhưng đều trở thành trường thường trong JSON;
  3. Nội dung hỗn hợp: các cấu trúc như <p>你好 <b>世界</b> 再见</p> không thể biểu diễn bằng object thuần;
  4. Khai báo namespace: xmlns là khái niệm phạm vi; một khi bị làm phẳng thành trường, phạm vi biến mất;
  5. CDATA và entity: <![CDATA[...]]> và &amp; trong JSON chẳng qua chỉ còn là văn bản.

2.3 Lưỡng nan "một phần tử hay một mảng"

<list><item>1</item></list>

Với một item duy nhất, xml2js mặc định trả về object và chỉ trả về mảng khi có hai phần tử trở lên. Hệ quả là mã front-end phải viết Array.isArray(x) ? x : [x].

Biện pháp: bật tuỳ chọn "luôn mảng" khi phân tích, hoặc khai báo trường là kiểu mảng dựa trên schema.

import { parseStringPromise } from 'xml2js';

const parsed = await parseStringPromise(xmlText, {
  explicitArray: true,   // return an array even for a single element
  trim: true,
  explicitRoot: false,
});

2.4 Nhắc nhở bảo mật

Trước khi phân tích XML không tin cậy, bạn buộc phải tắt external entity, nếu không có thể kích hoạt XXE (đọc tệp cục bộ, dò mạng nội bộ):

// In the Node ecosystem, DTDs and external entities are usually disabled through parser options
const parserOptions = { doctype: false, externalEntities: false };

XML → JSON chỉ là tầng chuyển đổi đầu tiên; chuyển đổi không xoá bỏ bề mặt tấn công mà XML mang theo.

3. Mỗi định dạng phù hợp ở đâu

Tình huống Định dạng đề xuất Lý do
API HTTP công khai JSON Được trình duyệt và mọi ngôn ngữ hỗ trợ sẵn
Cấu hình do con người duy trì YAML Comment, thụt lề, khả năng đọc
Tài liệu cần schema mạnh và namespace (SOAP, Office Open XML, SVG) XML Năng lực cấu trúc và hệ sinh thái sẵn có
Tương tác với công cụ vận hành (K8s, CI) YAML Mặc định của hệ sinh thái
Lưu trữ, cache, log JSON / JSONL Phổ quát và không phụ thuộc

4. Ghi chú triển khai

import { stringify as toYaml } from 'yaml';

/**
 * Converts a JSON value to YAML text while keeping Chinese characters unescaped.
 * @param value The JSON value to convert
 * @returns The YAML text
 */
function jsonToYaml(value: unknown): string {
  return toYaml(value, {
    indent: 2,
    lineWidth: 0,      // disable automatic wrapping so long strings are not split
    defaultStringType: 'QUOTE_DOUBLE',
  });
}

Một vài tuỳ chọn thực tế:

  • lineWidth: 0 tắt tự động xuống dòng; nếu không, URL dài bị cắt sang nhiều dòng và trở nên khó đọc;
  • đặt ngoặc kép cho mọi thứ giúp các giá trị như NO và 1.10 không bị hiểu nhầm;
  • với dữ liệu có độ chính xác số nhạy cảm, hãy chuyển sang chuỗi trước khi tuần tự hoá.

Với chiều XML, hãy cố định một hàm ánh xạ duy nhất và đưa "tiền tố thuộc tính", "tên khóa text" và "chiến lược mảng" vào chữ ký của nó thay vì rải rác khắp nơi:

type XmlMappingOptions = {
  attributePrefix: string;
  textKey: string;
  alwaysArray: boolean;
};

const defaultMapping: XmlMappingOptions = {
  attributePrefix: '@',
  textKey: '#text',
  alwaysArray: false,
};

5. Danh sách kiểm tra

  • [ ] Mọi định danh "trông giống số/boolean" trong YAML đều được đặt trong ngoặc;
  • [ ] Bộ mẫu kiểm chứng cho JSON → YAML → JSON được định nghĩa và bao phủ bằng test;
  • [ ] Khi phải giữ comment, dùng Document API thay vì khứ hồi qua object;
  • [ ] Quy ước ánh xạ XML được ghi lại và cả nhóm dùng chung một bộ tham số;
  • [ ] DTD và external entity bị tắt khi phân tích XML không tin cậy;
  • [ ] Số byte và số trường được so sánh trước và sau chuyển đổi, bất kỳ bất thường nào cũng phát cảnh báo.

FAQ

Làm sao chọn hướng chuyển đổi giữa JSON, YAML và XML?

Ưu tiên JSON làm định dạng trung gian: phân tích YAML và XML thành mô hình dữ liệu trước, rồi tuần tự hoá từ mô hình đó thay vì viết lại văn bản. Dùng Document API của YAML khi cần giữ comment, và cố định một quy ước ánh xạ XML trước khi bạn cần đến thuộc tính hoặc namespace.

Vì sao chuyển XML sang JSON lại làm mất thuộc tính và namespace?

JSON không có khái niệm thuộc tính hay namespace, nên thuộc tính XML, tiền tố và URI namespace buộc phải được gộp vào khóa thường, thường là với tiền tố @ hoặc node #text. Một khi quy tắc ánh xạ khác nhau, cùng một XML sẽ tạo ra JSON khác nhau và không thể khứ hồi.

Có nên giữ JSON, YAML và XML đồng bộ hai chiều lâu dài không?

Không. Comment, anchor, thuộc tính XML và namespace đều bị mất qua các định dạng, và đồng bộ hai chiều cứ tích lũy khác biệt. Hãy chọn một định dạng nguồn và coi các định dạng còn lại là bản xuất một lần hoặc góc nhìn tạm thời.

Tài liệu tham khảo

Bài viết liên quan

Chuyển đổi JSON sang và từ CSV cùng dữ liệu dạng bảng

CSV là hai chiều, còn JSON có dạng cây. Phần khó của chuyển đổi không nằm ở mã — mà ở việc quyết định trước xem mảng được làm phẳng thế nào và kiểu được giữ lại ra sao.

Người mớiĐọc khoảng 10 phútCập nhật 2026-10-30

Quản lý JSON cho cấu hình, fixture và dữ liệu cục bộ

Cấu hình phải được phân tầng và kiểm chứng; fixture phải tất định, dễ đọc và có thể so sánh diff. Nếu hai loại tệp này bị quản lý kém, chi phí xử lý sự cố sẽ cao trong thời gian dài.

Người mớiĐọc khoảng 10 phútCập nhật 2026-10-30

JSON sang YAML bằng dòng lệnh: bốn cái bẫy đằng sau một lệnh yq

Lệnh bị chép lại nhiều nhất trong mọi tutorial, yq -P input.json > output.yaml, trên yq v4 vẫn xuất ra JSON. Bài này đo yq v4.44.6, PyYAML 6.0.3 và js-yaml trên mẫu 2.39 MB, giải thích vì sao phần mở rộng đầu vào quyết định định dạng đầu ra, và đưa ra cách viết đúng cho chuyển đổi hàng loạt, dấu ngoặc, thụt lề và độ chính xác số nguyên lớn.

Trung cấpĐọc khoảng 9 phútCập nhật 2026-11-03