更新大的 JSON 對(duì)象,也能用上增量更新!
什么是 JSON Patch
JSON Patch[1] 是一種用于描述如何對(duì) JSON 文檔進(jìn)行更改的格式,它使用一系列簡(jiǎn)潔的操作指令來(lái)指示如何修改 JSON 數(shù)據(jù)。這些操作包括添加新的數(shù)據(jù)、刪除舊的數(shù)據(jù)、替換現(xiàn)有的數(shù)據(jù)或者移動(dòng)數(shù)據(jù)等等。
為什么使用 JSON Patch
在一個(gè)大型的 Web 應(yīng)用程序中,客戶端需要頻繁地與服務(wù)器通信,獲取最新的數(shù)據(jù)或者將修改后的數(shù)據(jù)提交給服務(wù)器。傳統(tǒng)的方式是每次更新都發(fā)送整個(gè) JSON 文檔,即使只有一小部分?jǐn)?shù)據(jù)發(fā)生了變化。這會(huì)導(dǎo)致網(wǎng)絡(luò)傳輸量大,增加了網(wǎng)絡(luò)延遲,同時(shí)也增加了服務(wù)器和客戶端的負(fù)載。
JSON Patch 提供了一種高效的解決方案來(lái)減少網(wǎng)絡(luò)傳輸量和提高數(shù)據(jù)更新效率。通過(guò)使用 JSON Patch,客戶端可以僅發(fā)送需要修改的部分?jǐn)?shù)據(jù),而不是整個(gè) JSON 文檔。服務(wù)器收到 JSON Patch 后,可以根據(jù)指令執(zhí)行相應(yīng)的操作,從而實(shí)現(xiàn)數(shù)據(jù)的增量更新。這樣既減少了網(wǎng)絡(luò)傳輸量,提高了網(wǎng)絡(luò)效率,又降低了服務(wù)器和客戶端的負(fù)載,同時(shí)也保證了數(shù)據(jù)的一致性。
JSON Patch 有哪些優(yōu)點(diǎn)
- 減少傳輸量:JSON Patch 只傳輸要對(duì) JSON 文檔進(jìn)行的具體更改,而不需要傳輸整個(gè) JSON 文檔。這樣可以節(jié)省網(wǎng)絡(luò)帶寬,尤其在大型數(shù)據(jù)集或者低速網(wǎng)絡(luò)環(huán)境下更為顯著。
- 增量更新:JSON Patch 支持對(duì) JSON 文檔進(jìn)行增量更新,這意味著你可以只發(fā)送需要修改的部分,而不是整個(gè)文檔。這對(duì)于實(shí)時(shí)應(yīng)用程序以及需要頻繁更新的情況非常有用。
- 原子性操作:JSON Patch 操作可以作為一個(gè)原子單元執(zhí)行,這意味著要么全部操作成功,要么全部失敗。這可以確保數(shù)據(jù)的一致性,避免因?yàn)椴糠指聦?dǎo)致數(shù)據(jù)不一致的情況。
- 靈活性和可擴(kuò)展性:JSON Patch 不限制你對(duì) JSON 文檔進(jìn)行的操作,你可以根據(jù)需求執(zhí)行添加、刪除、替換、移動(dòng)等各種操作,并且可以根據(jù)實(shí)際需求擴(kuò)展新的操作。
JSON Patch 支持哪些操作
1.add
向 JSON 文檔中添加新的值,需要指定路徑和要添加的值。
{ "op": "add", "path": "/path", "value": "new value" }
2.remove
從 JSON 文檔中移除一個(gè)值,需要指定要移除的值的路徑。
{ "op": "remove", "path": "/path" }
3.replace
替換 JSON 文檔中的一個(gè)值,需要指定要替換的值的路徑和新的值。
{ "op": "replace", "path": "/path", "value": "new value" }
4.move
移動(dòng) JSON 文檔中的一個(gè)值到另一個(gè)位置,需要指定要移動(dòng)的值的路徑和目標(biāo)路徑。
{ "op": "move", "from": "/oldpath", "path": "/newpath" }
5.copy
復(fù)制 JSON 文檔中的一個(gè)值到另一個(gè)位置,需要指定要復(fù)制的值的路徑和目標(biāo)路徑。
{ "op": "copy", "from": "/oldpath", "path": "/newpath" }
6.test
測(cè)試 JSON 文檔中的一個(gè)值是否等于給定的值,主要用于驗(yàn)證操作是否可以成功執(zhí)行。需要指定要測(cè)試的值的路徑和預(yù)期的值。
{ "op": "test", "path": "/path", "value": "expected value" }
如何使用 JSON Patch
很多常見(jiàn)的開(kāi)發(fā)語(yǔ)言,都實(shí)現(xiàn)了 JSON Patch 規(guī)范。在 JS 環(huán)境,我們可以使用 fast-json-patch[2] 這個(gè)庫(kù)。
首先,使用 npm 或 pnpm 來(lái)安裝 fast-json-patch:
npm install fast-json-patch
or
pnpm add fast-json-patch
成功安裝 fast-json-patch 庫(kù)之后,我們就可以利用它提供的 API 來(lái)實(shí)現(xiàn)以下功能:
- 比較兩個(gè)對(duì)象獲取補(bǔ)丁
- 觀察對(duì)象變化并在檢測(cè)到變化時(shí)生成補(bǔ)丁
- 在 JS 對(duì)象上應(yīng)用單個(gè)或多個(gè)補(bǔ)丁
- 驗(yàn)證補(bǔ)丁序列
1.比較兩個(gè)對(duì)象獲取補(bǔ)丁
import { compare } from "fast-json-patch/index.mjs";
const documentA = { user: { firstName: "Albert", lastName: "Einstein" } };
const documentB = { user: { firstName: "Albert", lastName: "Collins" } };
const diff = compare(documentA, documentB);
/**
* diff:
* [ { op: 'replace', path: '/user/lastName', value: 'Collins' } ]
*/
2.觀察對(duì)象變化并在檢測(cè)到變化時(shí)生成補(bǔ)丁
import { generate, observe } from "fast-json-patch/index.mjs";
const document = {
firstName: "Joachim",
lastName: "Wester",
contactDetails: { phoneNumbers: [{ number: "555-123" }] },
};
const observer = observe(document);
document.firstName = "Albert";
document.contactDetails.phoneNumbers[0].number = "123";
document.contactDetails.phoneNumbers.push({ number: "456" });
const patch = generate(observer);
/**
* patch:
* [
* {
* op: 'replace',
* path: '/contactDetails/phoneNumbers/0/number',
* value: '123'
* },
* {
* op: 'add',
* path: '/contactDetails/phoneNumbers/1',
* value: { number: '456' }
* },
* { op: 'replace', path: '/firstName', value: 'Albert' }
* ]
*/
3.在 JS 對(duì)象上應(yīng)用單個(gè)或多個(gè)補(bǔ)丁
應(yīng)用單個(gè)補(bǔ)丁
import { applyPatch } from "fast-json-patch/index.mjs";
const documentA = { user: { firstName: "Albert", lastName: "Einstein" } };
const patchedResult = applyPatch(documentA, [
{ op: "replace", path: "/user/lastName", value: "Collins" },
]);
/**
* patchedResult[0]:
* {
* newDocument: { user: { firstName: 'Albert', lastName: 'Collins' } },
* removed: 'Einstein'
* }
*/
應(yīng)用多個(gè)補(bǔ)丁
import { applyPatch } from "fast-json-patch/index.mjs";
const document = {
firstName: "Joachim",
lastName: "Wester",
contactDetails: { phoneNumbers: [{ number: "555-123" }] },
};
const patchedResult = applyPatch(document, [
{
op: "replace",
path: "/contactDetails/phoneNumbers/0/number",
value: "123",
},
{
op: "add",
path: "/contactDetails/phoneNumbers/1",
value: { number: "456" },
},
{ op: "replace", path: "/firstName", value: "Albert" },
]);
/**
* patchedResult[0]:
* {
* newDocument: {
* firstName: "Albert",
* lastName: "Wester",
* contactDetails: { phoneNumbers: [{ number: "123" }, { number: "456" }] },
* },
* removed: "555-123",
* }
*/
4.驗(yàn)證補(bǔ)丁序列
import { validate } from "fast-json-patch/index.mjs";
const documentA = { user: { firstName: "Albert", lastName: "Einstein" } };
const validatedResult = validate(
[{ op: "replace", path: "/user/lastName", value: "Collins" }],
documentA
);
如果補(bǔ)丁序列不滿足 JSON Patch 規(guī)范,在驗(yàn)證過(guò)程中就會(huì)拋出 JsonPatchError 異常對(duì)象。
JSON Patch 和 fast-json-patch 的相關(guān)內(nèi)容就介紹到這里,感興趣的話,可以嘗試一下 JSON Patch。
參考資料
[1]JSON Patch: https://datatracker.ietf.org/doc/html/rfc6902
[2]fast-json-patch: https://github.com/Starcounter-Jack/JSON-Patch