Tim Wang Tech Blog

JSON Patch and JSON Merge Patch

JSON Patch and JSON Merge Patch 的中文翻译版本。

作为近年来受到关注HTTP的 “PATCH” 方法的带动,人们开始提出关于表示 JSON 驱动的 PATCH 格式的想法,它以声明方式描述了两个 JSON 文档之间的差异。 目前已经有许多解决方案被提出,不计其数。而其中IETF 已经发布了两种格式作为 RFC 文档来解决这个问题:RFC 6902 (JSON Patch)RFC 7396 (JSON Merge Patch)。两者都有优缺点,会根据不同的用例因人而异,所以让我们快速比较,看看你会使用哪一个。

JSON Patch

JSON Patch format 和数据库事务类似:它是JSON 文档上的更改操作,由适当的实现自动执行。它基本上是一系列 "add", "remove", "replace", "move""copy" 操作。

让我们通过一个简单的例子来看看JSON 文档:

{
	"users" : [
		{ "name" : "Alice" , "email" : "alice@example.org" },
		{ "name" : "Bob" , "email" : "bob@example.org" }
	]
}

我们可以通过PATCH操作更新它,这次操作更改了Alice的邮件地址,然后添加了一个新的元素到数组中:

[
	{
		"op" : "replace" ,
		"path" : "/users/0/email" ,
		"value" : "alice@wonderland.org"
	},
	{
		"op" : "add" ,
		"path" : "/users/-" ,
		"value" : {
			"name" : "Christine",
			"email" : "christine@example.org"
		}
	}
]

然后结果会变成:

{
	"users" : [
		{ "name" : "Alice" , "email" : "alice@wonderland.org" },
		{ "name" : "Bob" , "email" : "bob@example.org" },
		{ "name" : "Christine" , "email" : "christine@example.org" }
	]
}

所以 JSON Patch 中的操作大纲是:

  • "op" 代表的操作类型
  • 由其他键描述的操作参数
  • "path" 参数指向操作的目标文档片段

一个有趣的选项是 JSON Patch 的 "test" 操作符:它的执行不会产生任何副作用,所以它不是一个数据操作符。而是用来描述文档的断言,如果 "test" 返回 false,那么会发生错误,后续的操作不会执行,文档会回到初始状态。我觉得 "test" 对于在补丁执行之前检查先决条件很有用,用来检查执行后的文档是否正常。JSON Patch 是一个原子性的操作,所以如果 "test" 发现文档不一致,那么你可以认为文档已经回到初始状态。

JSON Merge Patch

除了 JSON Patch 之外,还有另一种 JSON 格式,JSON Merge Patch - RFC 7386,也可以用于相同的目的。JSON Merge Patch 和 JSON Patch 的主要区别是,JSON Merge Patch 类似于 diff 文件,它只包含文档中应该变化的节点。

请看一个简单的例子数据源,假设我们有以下文档:

{
	"a": "b",
	"c": {
		"d": "e",
		"f": "g"
	}
}

我们可以通过下面的内容patch它

{
	"a":"z",
	"c": {
		"f": null
	}
}

它将改变 "a" 的值为 "z",并将 "f" 键删除。

乍一看,这个格式可能会看起来很更简便一些,因为大多数人都能够直接理解原始文档的格式。因为很可能任何了解原始文档架构的人也会立即理解merge patch文档。 它只是一个标准化的一个可以自然地称为一个 JSON 文档的PATCH。

但这种简单性也有一些限制:

  • 删除是通过设置键的值为 null 来实现的。这意味着不能通过改变键的值来删除键,因为这种改变不能被merge patch描述。
  • 数组不能被merge patch操作。如果你想添加一个元素到数组,或者改变数组中的任何元素,那么你必须包含整个数组在merge patch文档中,即使是微小的改动。
  • merge patch不会导致错误。任何不正确的patch都会被合并,所以它是一个很严格的格式。它不一定是好的,因为你可能需要在合并后进行程序性检查,或者在合并后运行JSON Schema验证。

总结(Summary)

JSON Merge Patch是一个简易的patch操作,但是它也有明显的局限性。如果你正在建造一个小的项目,使用简单的JSON Schema,它可能是一个很好的选择。但你希望提供一个可以被客户端读取的快速易懂,较为可用的方法来更新JSON文档。面向公共消费而设计但没有适当的客户端库的 REST API 可能是一个更好的选择。

对于更加复杂的使用场景,我会偏向于JSON Patch,因为它适用于任何JSON文档(不论是merge patch,它不能设置键为 null)。该规范也保证了原子执行和robust的错误报告。