原文地址https://tools.ietf.org/html/rfc6902
1.简介
JavaScript Object Notation (JSON) [RFC4627]是一种交换和存储的结构化数据常见的格式。HTTP PATCH [RFC5789]方法对资源进行部分修改,扩展了超文本传输协议(HTTP)[RFC2616]。
JSON PATCH 是一种格式(由媒体类型"application/ json-patch+json"标识)用于表示要应用到目标JSON文档的操作序列。它适合用于HTTP PATCH 方法。
这种格式在其他需要对JSON文档或具有类似约束的数据结构进行部分更新的情况下也可能有用(也就是说,可以使用JSON语法将其序列化为对象或数组。)。
2.约定
关键词"MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL"在本文档中解释为RFC 2119[RFC2119]中描述的内容。
3.文档结构
一个JSON Patch文档是一个JSON文档[RFC4627]表现为一个对象数组。每个对象代表一个被应用到目标JSON文档的操作。
下面是一个JSON Patch示例文档,传输HTTP Patch请求:
PATCH /my/data HTTP/1.1
Host: example.org
Content-Length: 326
Content-Type: application/json-patch+json
If-Match: "abc123"
[
{ "op": "test", "path": "/a/b/c", "value": "foo" },
{ "op": "remove", "path": "/a/b/c" },
{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] },
{ "op": "replace", "path": "/a/b/c", "value": 42 },
{ "op": "move", "from": "/a/b/c", "path": "/a/b/d" },
{ "op": "copy", "from": "/a/b/d", "path": "/a/b/e" }
]
针对目标JSON文档,开始对JSON Patch进行计算。操作按照数组中出现的顺序依次被应用。序列中的每个操作都应用于目标文档;结果文档成为下一个操作的目标。计算过程一直持续到成功地应用所有操作或者直到遇到错误条件。
4.操作
操作对象必须有一个“op”成员,其值表示要执行的操作。它的值必须是"add", "remove", "replace", "move", "copy", 或"test"中的一个;其他值是错误的。下面是每个对象的语义定义。
此外,操作对象必须有一个精确的“path”成员。值是一个包含JSON-Pointer值的字符串[RFC6901],这个值引用目标文档(the "target location")中的一个位置执行操作。
其他操作对象成员的含义由操作定义(见下面的小节)。未被明确定义为操作成员的问题必须忽略(也就是说,操作将完成,就像未定义的成员没有出现在对象中)。
注意,JSON对象中成员的顺序并不重要; 因此,以下操作对象是等价的:
{ "op": "add", "path": "/a/b/c", "value": "foo" }
{ "path": "/a/b/c", "op": "add", "value": "foo" }
{ "value": "foo", "path": "/a/b/c", "op": "add" }
操作应用于由JSON文档表示的数据结构,也就是说,在任何非转义的情况下(参见RFC4627,第2.5小节) 发生。
4.1.add
“add”操作执行以下功能之一,取决于目标位置引用的内容:
- 如果目标位置指定一个数组索引,那么新值就是在指定的索引中插入数组。
- 如果目标位置指定不存在的对象成员,则将新成员添加到对象中。
- 如果目标位置指定存在的对象成员,该成员的值被替换。
- 操作对象必须包含一个“value”成员,其内容指定要添加的值。
例如:{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }
应用操作时,目标位置必须引用:
- 目标文档的根 - 然后指定的值成为目标文档的整个内容。
- 添加到现有对象的成员 - 然后在指定位置将提供的值添加到该对象。如果成员已经存在,则由指定值替换。
- 添加到现有数组的元素 - 因此提供的值添加到数组的指定位置。在指定索引上的任何元素都会向右侧移动一个位置。指定的索引不能大于数组中元素的个数。如果-”字符用于索引数组的末尾(见[RFC6901]),这就产生将值追加到数组中的作用。
因为这个操作被设计用来增加现有的对象和数组,其目标位置通常会不存在。尽管指针的错误处理算法将被调用,该规范定义了“add”的错误处理行为忽略指针错误并添加指定的值。
然而,对象本身或包含它的数组需要存在,如果不是这样的话,它仍然是一个错误,。以"/a/b"开始的目标位置的“add”文档:
{ "a": { "foo": 1 } }
这不是一个错误,因为“a”存在,并且“b”将被添加到它的值中。
这是本文档中的一个错误:
{ "q": { "bar": 2 } }
因为“a”不存在。
4.2. remove
“remove”操作删除目标位置的值。要操作成功,目标位置必须存在。
例如: { "op": "remove", "path": "/a/b/c" }
如果从数组中删除元素,指定索引上的任何元素都会向左移动一个位置。
4.3 . replace
"replace"操作用新值替换目标位置上的值。操作对象必须包含“value”成员,其内容指定替换值。
要操作成功,目标位置必须存在。
例如:{ "op": "replace", "path": "/a/b/c", "value": 42 }
此操作在功能上与值的“remove”操作完全相同,紧接着在相同的位置使用替换值进行“add”操作。
4.4. move
"move"操作清除指定值并添加它到目标位置。
操作对象必须包含“from"成员,它是一个包含JSON Pointer值的字符串,该值引用目标文档中的位置以从中移动值。
要操作成功,‘’from'位置必须存在。
例如:
{ "op": "move", "from": "/a/b/c", "path": "/a/b/d" }
这个操作在功能上与在"from"位置的”remove“操作相同,紧接着在目标位置使用刚移除的值进行”add“操作。
”from“位置不能是”path“位置的真正的前缀。也就是说,不能将位置移动到其子节点中。
4.5. copy
"copy"操作复制指定位置的值到目标位置。
操作对象必须包含“from"成员,它是一个包含JSON Pointer值的字符串,该值引用目标文档中的位置以从中复制值。
要操作成功,‘’from'位置必须存在。
例如:
{ "op": "copy", "from": "/a/b/c", "path": "/a/b/e" }
这个操作在功能上与在目标位置使用”from“成员中指定值的”add“操作相同。
4.6. test
"test"操作测试目标位置的值与指定的值相等。
操作对象必须包含“value”成员,传达值与目标位置的值进行比较。
目标位置必须等于该值的“value”, 操作才被认为是成功的。
这里,“equal”表示目标位置的值和传达的“value”是相同的JSON类型,并且它们被认为等于下列规则的类型:
- strings:如果被认为是相等的,它们包含相同数量的Unicode字符及其代码点逐字节相等
- numbers:如果它们的数值相等,则被认为是相等的。
- arrays:如果它们包含相同数量的值,并且每个值可以视为等于另一个数组中相应位置的值,使用此特定类型规则的list,则被认为是相等的
- objects:如果它们包含相同数量的成员,并且如果每个成员可以被认为是等于另一对象中的成员,通过比较他们的键(如字符串)和它们的值(使用此特定类型规则的list),那么被认为是相等的,
- literals (false, true, and null):如果他们都是相同的,则被认为是相等的。
注意,此比较是一个逻辑比较;例如,数组的成员值之间的空格并不重要。
另外,注意对象成员序列化的顺序不重要。
例如: { "op": "test", "path": "/a/b/c", "value": "foo" }
5. 错误处理
如果一个规范要求违反了JSON Patch文档,或者如果一个操作不成功,那么JSON Patch文档的计算应该终止,整个Patch文档的应用不应被认为是成功的。
查看[RFC5789],2.2小节是关于处理错误的注意事项,当通过HTTP Patch方法使用JSON Patch时,包括建议使用状态码来表示各种条件。
注意,HTTP PATCH方法是原子性的,依据[RFC5789]。因此,以下patch会导致文档没有被更改(因为“test”操作会导致错误):
[
{ "op": "replace", "path": "/a/b/c", "value": 42 },
{ "op": "test", "path": "/a/b/c", "value": "C" }
]
6. IANA Considerations
JSON Patch文档的互联网媒体类型是application/json-patch+json.
类型名称(Type name): application
子类型名称(Subtype name): json-patch+json
必需的参数(Required parameters): none
可选的参数(Optional parameters): none
编号考量(Encoding considerations): binary
安全注意事项:参见第7节中的安全注意事项。
互操作性问题:N / A
发布说明书:
RFC 6902
使用这种媒体类型的应用程序:
操作JSON文档的应用程序。
附加信息:
Magic number(s): N/A
文件扩展名File extension(s): .json-patch
苹果电脑文件类型代码Macintosh file type code(s): TEXT
个人及电子邮件地址,取系以获取更多的信息:
保罗·c·布莱恩< [email protected] >
用途Intended usage: COMMON
使用限制Restrictions on usage: none
作者Author: Paul C. Bryan [email protected]
变更管理Change controller: IETF
7. Security Considerations安全性考量
该规范与JSON[RFC4627]和JSON-Pointer [RFC6901]具有相同的安全性考量。
一些老的Web浏览器可以强制加载一个随意的根为数组的JSON文档,从而导致一个包含敏感信息的JSON Patch文档可能被暴露给攻击者的情况,即使访问是经过验证的。这就是所谓的跨站点请求伪造(CSRF)攻击 [CSRF]。
然而,这样的浏览器并没有被广泛使用(在撰写本文时,估计它们在不到1%的市场中使用)。尽管如此,发布者仍担心这种攻击,应避免使用http GET提供此类文档。
8. 致谢
以下的人员为本规范提供了想法、反馈和措辞:
Mike Acar, Mike Amundsen, Cyrus Daboo, Paul Davis, Stefan Koegl, Murray S. Kucherawy, Dean Landolt, Randall Leeds, James Manger, Julian Reschke, James Snell, Eli Stevens, and Henry S. Thompson.
JSON Patch文档的结构受到了XMLPatch 文档规范的影响[RFC5261]。
9.参考文献
9.1. 标准参考
[RFC2119] Bradner, S.,(在RFC中使用来表示需求级别的关键字)BCP 14,RFC2119,1997年3月。
[RFC4627] Crockford,D.,(JavaScript Object Notation (JSON)的application/json媒体类型)RFC4627,2006年7月
[RFC6901] Bryan, P., Ed., Zyp, K., and M. Nottingham, Ed.,JavaScript Object Notation (JSON) Pointer",RFC 6901,2013年4月
9.2参考资料
[CSRF] Barth, A., Jackson, C., and J. Mitchell, "Robust Defenses for Cross-Site Request Forgery", ACM Conference on Computer and Communications Security, October 2008, http://seclab.stanford.edu/websec/csrf/csrf.pdf.
[RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.
[RFC5261] Urpalainen, J., "An Extensible Markup Language (XML) Patch Operations Framework Utilizing XML Path Language (XPath) Selectors", RFC 5261, September 2008.
[RFC5789] Dusseault, L. and J. Snell, "PATCH Method for HTTP",RFC 5789, March 2010.
附录a .示例
A.1. 添加对象成员(Adding an Object Member)
目标JSON文档示例(An example target JSON document):
{ "foo": "bar"}
JSON Patch文档(A JSON Patch document):
[
{ "op": "add", "path": "/baz", "value": "qux" }
]
生成的JSON文档(The resulting JSON document):
{
"baz": "qux",
"foo": "bar"
}
A.2. 添加数组元素(Adding an Array Element)
目标JSON文档示例:
{ "foo": [ "bar", "baz" ] }
JSON Patch文档: [ { "op": "add", "path": "/foo/1", "value": "qux" } ]
生成的JSON文档: { "foo": [ "bar", "qux", "baz" ] }
A.3. 移除一个对象成员(Removing an Object Member)
目标JSON文档示例:
{
"baz": "qux",
"foo": "bar"
}
JSON Patch文档:
[ { "op": "remove", "path": "/baz" } ]
生成的JSON文档: { "foo": "bar" }
A.4. 移除一个数组元素Removing an Array Element
目标JSON文档示例:
{ "foo": [ "bar", "qux", "baz" ] }
JSON Patch文档:
[
{ "op": "remove", "path": "/foo/1" }
]
生成的JSON文档:
{ "foo": [ "bar", "baz" ] }
A.5. 替换值(Replacing a Value)
目标JSON文档示例:
{
"baz": "qux",
"foo": "bar"
}
JSON Patch文档:
[
{ "op": "replace", "path": "/baz", "value": "boo" }
]
生成的JSON文档:
{
"baz": "boo",
"foo": "bar"
}
A.6. 移动值(Moving a Value)
目标JSON文档示例:
{
"foo": {
"bar": "baz",
"waldo": "fred"
},
"qux": {
"corge": "grault"
}
}
JSON Patch文档:
[
{ "op": "move", "from": "/foo/waldo", "path": "/qux/thud" }
]
生成的JSON文档:
{
"foo": {
"bar": "baz"
},
"qux": {
"corge": "grault",
"thud": "fred"
}
}
A.7. 移动数组元素(Moving an Array Element)
目标JSON文档示例:
{ "foo": [ "all", "grass", "cows", "eat" ] }
JSON Patch文档:
[
{ "op": "move", "from": "/foo/1", "path": "/foo/3" }
]
生成的JSON文档:
{ "foo": [ "all", "cows", "eat", "grass" ] }
A.8. 测试值:成功(Testing a Value: Success)
目标JSON文档示例:
{
"baz": "qux",
"foo": [ "a", 2, "c" ]
}
将计算成功的JSON Patch文档:
[
{ "op": "test", "path": "/baz", "value": "qux" },
{ "op": "test", "path": "/foo/1", "value": 2 }
]
A.9. 测试值:错误(Testing a Value: Error)
目标JSON文档示例:
{ "baz": "qux" }
将导致错误状态的JSON Patch文档:
[
{ "op": "test", "path": "/baz", "value": "bar" }
]
A.10. 添加嵌套成员对象(Adding a Nested Member Object)
目标JSON文档示例:
{ "foo": "bar" }
JSON Patch文档:
[
{ "op": "add", "path": "/child", "value": { "grandchild": { } } }
]
生成的JSON文档:
{
"foo": "bar",
"child": {
"grandchild": {
}
}
}
A.11. 忽略无法识别的元素(Ignoring Unrecognized Elements)
目标JSON文档示例:
{ "foo": "bar" }
JSON Patc文档:
[
{ "op": "add", "path": "/baz", "value": "qux", "xyz": 123 }
]
生成的JSON文档:
{
"foo": "bar",
"baz": "qux"
}
A.12. 添加到不存在的目标(Adding to a Nonexistent Target)
目标JSON文档示例:
{ "foo": "bar" }
JSON Patc文档:
[
{ "op": "add", "path": "/baz/bat", "value": "qux" }
]
这个JSON Patch文档,应用于目标JSON文档上面,会导致一个错误(因此,它不会被应用),因为“add”操作的目标位置,既不引用文档的根,也不是现有对象的成员,也不是现有数组的成员。
A.13. 无效的JSON Patch文档(Invalid JSON Patch Document)
JSON Patc文档:
[
{ "op": "add", "path": "/baz", "value": "qux", "op": "remove" }
]
这个JSON Patch文件不能被视为一个“add”操作,因为它包含后面的"op":"remove"元素。JSON要求对象成员的名称是具有唯一的“SHOULD”要求,并且副本没有的标准错误处理。
A.14. 转义排序( Escape Ordering)
目标JSON文档示例:
{
"/": 9,
"~1": 10
}
JSON Patc文档:
[
{"op": "test", "path": "/~01", "value": 10}
]
生成的JSON文档:
{
"/": 9,
"~1": 10
}
A.15. 比较字符串和数字(Comparing Strings and Numbers)
目标JSON文档示例:
{
"/": 9,
"~1": 10
}
JSON Patc文档:
[
{"op": "test", "path": "/~01", "value": "10"}
]
这会导致一个错误,因为测试失败了。文档的值是数值,而被测试值是一个字符串。
A.16. 添加数组值(Adding an Array Value)
目标JSON文档示例:
{ "foo": ["bar"] }
JSON Patc文档:
[
{ "op": "add", "path": "/foo/-", "value": ["abc", "def"] }
]
生成的JSON文档:
{ "foo": ["bar", ["abc", "def"]] }
作者的地址
Paul C. Bryan (editor)
Salesforce.com
Phone: +1 604 783 1481
EMail: [email protected]
Mark Nottingham (editor)
Akamai
EMail: [email protected]