该风格指南是对在google创建JSON APIs而提供的指导性准则和建议,总体来说,JSON APIs符合JSON.org的规范。这份风格指南澄清和标准化了特定情况。
一、一般准则
1.注释
JSON对象中不应该包括注释。
2.双引号
如果某个属性需要引号,则必须使用双引号。所以的属性名都必须在双引号内,字符类型的属性值必须使用双引号。其他类型值,比如 布尔或者数字,不应该使用双引号。
3.扁平化数据 VS 结构层次
不能为了方便而将数据任意分组。JSOIN中的数据应该以扁平化的方式呈现,不能为了方便而将数据任意分组。但在某些情况下,比如描述单一结构的一批属性,因为它被用来保持结构层次,因而是有意义的。但是遇到这种情况还是应当慎重你考虑,记住只有语义上有意义的时候才使用它。例如,一个地址可以有两种方式,但结构化的方式对开发人员更有意义:
扁平化地址:
{
"company": "Google",
"website": "http://www.google.com/",
"addressLine1": "111 8th Ave",
"addressLine2": "4th Floor",
"state": "NY",
"city": "New York",
"zip": "10011"
}
结构化地址:
{
"company": "Google",
"website": "http://www.google.com/",
"address": {
"line1": "111 8th Ave",
"line2": "4th Floor",
"state": "NY",
"city": "New York",
"zip": "10011"
}
}
二、属性名规则
1.属性名格式
选择有意义的属性名
属性名必须遵守以下准则:
- 属性名应该是具有定义语义的有意义的名称
- 属性名必须是驼峰式的 ASCII码字符串
- 首字符必须是字母,下划线_ 或者是 符号
- 随后的其他字符可以是字母、数字、下划线或者$
- 应该避免使用javascript中的保留关键字
{
"thisPropertyIsAnIdentifier": "identifier value"
}
2.JSON MAP中的键名
在JSON MAP中键名可以使用任意的Unicode字符。
但JSON对象作为MAP使用时,属性的名称命名规范并不使用。MAP是一个具有任意键值对的数据类型。MAP的键名不一定要遵循属性名称的命名准则,键名可以包含任意的unicode字符。客户端可以使用maps熟悉的方括号来访问这些属性
{
// "address" 属性是一个子对象
// 包含地址的各部分.
"address": {
"addressLine1": "123 Anystreet",
"city": "Anytown",
"state": "XX",
"zip": "00000"
},
// "thumbnails" 是一个映射
// 含有响应规格所对应的URL,用来映射thumbnail url的像素规格
"thumbnails": {
"72": "http://url.to.72px.thumbnail",
"144": "http://url.to.144px.thumbnail"
}
}
3.单数属性名 VS 复数属性名
数组类型应该是复数属性名,其他属性名都应该是单数。
{
// 单数
"author": "lisa",
// 一组同胞, 复数
"siblings": [ "bart", "maggie"],
// "totalItem" 看起来并不对
"totalItems": 10,
// 但 "itemCount" 要好些
"itemCount": 10,
}
4.命名冲突
通过选择新的属性名或API版本化来避免命名冲突
新的属性可能在将来被添加进保留列表中,JSON中不存在命名空间,如果存在命名冲突,可以通过选择新的属性名或者版本化来解决这个问题。例如,假设我们从下面的对象开始:
{
"apiVersion": "1.0",
"data": {
"recipeName": "pizza",
"ingredients": ["tomatoes", "cheese", "sausage"]
}
}
如果我们希望将来把ingredients列为保留字,我们可以通过下面两件事情来达成。
1.选一个不同的名字
{
"apiVersion": "1.0",
"data": {
"recipeName": "pizza",
"ingredientsData": "Some new property",
"ingredients": ["tomatoes", "cheese", "sausage"]
}
}
2.在主版本上重新命名属性
{
"apiVersion": "2.0",
"data": {
"recipeName": "pizza",
"ingredients": "Some new property",
"recipeIngredients": ["tomatos", "cheese", "sausage"]
}
}
三、属性值准则
1.属性值格式
属性值必须是unicode的布尔、数字、字符串、对象、数组或者null。
好的例子:
{
"canPigsFly": null, // null
"areWeThereYet": false, // boolean
"answerToLife": 42, // number
"name": "Bart", // string
"moreData": {}, // object
"things": [] // array
}
不好的例子:
{
"aVariableName": aVariableName, // Bad - JavaScript 标识符
"functionFoo": function() { return 1; } // Bad - JavaScript 函数
}
2.空或null的属性值
如果一个属性是可选的或者包含空值或null值,考虑从JSON中去掉该属性,除非它的存在有很强的语义。
{
"volume": 10,
// 即使 "balance" 属性值是零, 它也应当被保留,
// 因为 "0" 表示 "均衡"
// "-1" 表示左倾斜和"+1" 表示右倾斜
"balance": 0,
// "currentlyPlaying" 是null的时候可被移除
// "currentlyPlaying": null
}
3.枚举值
枚举值应该以字符串的形式出现。随着APIs的发展,枚举值可能被添加,移除或者改变。将枚举值当做字符串可以使下游用户优雅的处理枚举值的变更。
Java代码:
public enum Color {
WHITE,
BLACK,
RED,
YELLOW,
BLUE
}
JSON对象:
{
"color": "WHITE"
}
四、JSON结构和保留属性名
为了使APIs保持一致的借口,JSON对象应当使用以下的结构。该结构适用于JSON的请求和响应。在这个结构中,某些属性名将被保留用作特殊用途。这些属性并不是必需的,也就是说,每个保留的属性可能出现零次或一次。但是如果服务需要这些属性,建议遵循该命名条约。下面是一份JSON结构语义表,以Orderly格式呈现(现在已经被纳入 JSONSchema)。
object {
string apiVersion?;
string context?;
string id?;
string method?;
object {
string id?
}* params?;
object {
string kind?;
string fields?;
string etag?;
string id?;
string lang?;
string updated?; # date formatted RFC 3339
boolean deleted?;
integer currentItemCount?;
integer itemsPerPage?;
integer startIndex?;
integer totalItems?;
integer pageIndex?;
integer totalPages?;
string pageLinkTemplate /^https?:/ ?;
object {}* next?;
string nextLink?;
object {}* previous?;
string previousLink?;
object {}* self?;
string selfLink?;
object {}* edit?;
string editLink?;
array [
object {}*;
] items?;
}* data?;
object {
integer code?;
string message?;
array [
object {
string domain?;
string reason?;
string message?;
string location?;
string locationType?;
string extendedHelp?;
string sendReport?;
}*;
] errors?;
}* error?;
}*;
一些顶级保留属性名(限于篇幅,只列出少量几个):
(1) apiVersion
属性值类型:字符串
父节点:-
呈现请求中服务API期望的版本,以及在响应中保存的服务API版本。应随时提供apiVersion。这与数据的版本无关,将数据版本花应该通过其他机制来完成,比如etag。示例: {"apiVersion" : "2.1"}
(2) context
属性值类型:字符串
父节点:-
客户端设置这个值,服务器通过数据做出回应。这在JSON-P和批处理中很有用,用户可以使用context将响应与请求关联起来,该属性是顶级属性,因为不管响应是成功还是有错误,context总应该被呈现出来。context不同于id在cointext由用户提供而id由服务器分配。
示例:
请求 #1:
http://www.google.com/myapi?context=bart
请求 #2:
http://www.google.com/myapi?context=lisa
响应 #1:
{
"context": "bart",
"data": {
"items": []
}
}
响应 #2:
{
"context": "lisa",
"data": {
"items": []
}
}
公共的JavaScript处理器通过编码同时处理以下两个响应:
function handleResponse(response) {
if (response.result.context == "bart") {
// 更新页面中的 "Bart" 部分。
} else if (response.result.context == "lisa") {
// 更新页面中的 "Lisa" 部分。
}
}
(3)id
属性值类型:字符串
父节点:-
服务提供用于识别响应的标识(无论是请求成功还是有错误)。这对于将服务日志和单独收到的响应对应起来很有用。示例: {"id" : "1"}
(4)method
属性值类型:字符串
父节点:-
表示对数据即将执行或者已经被执行的动作。在JSON请求的情况下,method属性可以用来指明对数据进行何种操作。一个JSON-RPC请求的例子,其中method属性表示要在params上执行的操作:
{
"method": "people.get",
"params": {
"userId": "@me",
"groupId": "@self"
}
}
(5)params
属性值类型:对象
父节点:-
这个对象作为输入参数的映射发送给RPC请求。它可以和method属性一起用来执行RPC功能。若RPC方法不需要参数,则可以省略该属性。
{
"method": "people.get",
"params": {
"userId": "@me",
"groupId": "@self"
}
}
(6)data
属性值类型:对象
父节点:-
包含响应的所有数据。giant属性本身拥有许多保留属性名。服务可以自由的将自己的数据添加到这个对象,一个JSON响应要么应该包含一个data对象,要么应该包含一个error对象,但是不能两者都包含。如果data和error同时出现,则error对象优先。
(7)error
属性值类型:对象
父节点:-
表明错误发生,提供错误的详细信息。错误的格式支持从一个服务返回一个或多个错误。一个JSON响应可以有一个data对象或者一个error对象,但不能两者都包含。如果data和error都出现,则error优先。
{
"apiVersion": "2.0",
"error": {
"code": 404,
"message": "File Not Found",
"errors": [{
"domain": "Calendar",
"reason": "ResourceNotFoundException",
"message": "File Not Found
}]
}
}
五、示例
以下为 Youtube JSON API响应对象的示例:
{
"apiVersion": "2.0",
"data": {
"updated": "2010-02-04T19:29:54.001Z",
"totalItems": 6741,
"startIndex": 1,
"itemsPerPage": 1,
"items": [
{
"id": "BGODurRfVv4",
"uploaded": "2009-11-17T20:10:06.000Z",
"updated": "2010-02-04T06:25:57.000Z",
"uploader": "docchat",
"category": "Animals",
"title": "From service dog to SURFice dog",
"description": "Surf dog Ricochets inspirational video ...",
"tags": [
"Surf dog",
"dog surfing",
"dog",
"golden retriever",
],
"thumbnail": {
"default": "http://i.ytimg.com/vi/BGODurRfVv4/default.jpg",
"hqDefault": "http://i.ytimg.com/vi/BGODurRfVv4/hqdefault.jpg"
},
"player": {
"default": "http://www.youtube.com/watch?v=BGODurRfVv4&feature=youtube_gdata",
"mobile": "http://m.youtube.com/details?v=BGODurRfVv4"
},
"content": {
"1": "rtsp://v5.cache6.c.youtube.com/CiILENy73wIaGQn-Vl-0uoNjBBMYDSANFEgGUgZ2aWRlb3MM/0/0/0/video.3gp",
"5": "http://www.youtube.com/v/BGODurRfVv4?f=videos&app=youtube_gdata",
"6": "rtsp://v7.cache7.c.youtube.com/CiILENy73wIaGQn-Vl-0uoNjBBMYESARFEgGUgZ2aWRlb3MM/0/0/0/video.3gp"
},
"duration": 315,
"rating": 4.96,
"ratingCount": 2043,
"viewCount": 1781691,
"favoriteCount": 3363,
"commentCount": 1007,
"commentsAllowed": true
}
]
}
}
分页示例
{
"apiVersion": "2.1",
"id": "1",
"data": {
"query": "chicago style pizza",
"time": "0.1",
"currentItemCount": 10,
"itemsPerPage": 10,
"startIndex": 11,
"totalItems": 2700000,
"nextLink": "http://www.google.com/search?hl=en&q=chicago+style+pizza&start=20&sa=N"
"previousLink": "http://www.google.com/search?hl=en&q=chicago+style+pizza&start=0&sa=N",
"pagingLinkTemplate": "http://www.google.com/search/hl=en&q=chicago+style+pizza&start={index}&sa=N",
"items": [
{
"title": "Pizz'a Chicago Home Page"
// More fields for the search results
}
// More search results
]
}
}