[原文] [译] https://rollout.io/blog/json-ld-building-meaningful-data-apis/

每个人都喜欢 JSON

然而,JSON 本身是相当无意义的。好吧,它是有意义的,但这仅对这种格式的源创建者来说。它们可以通过文档、对话或使用来共享这一含义,但这些含义通常存在于与您正在查看的JSON文档相距甚远的地方。

如果意义本身被直接编码到文档中呢?如果 JSON 文档中的每个键都有自己独特的标识,您可以查找(因为web)并阅读其含义,那会怎么样?这不是很狡猾吗?

[推特:如果 JSON 文档中的每个键都有它唯一的标识,你可以查找并阅读,会怎么样呢?]

当然,实际上这是相当有可能的。

下面是一个相当典型的“人”信息,用JSON写成英文键名(为了匹配本文的内容):

1
2
3
4
5
6
{
  "first_name": "Benjamin",
  "last_name": "Young",
  "alias": "BigBlueHat",
  "email": "byoung@bigbluehat.com"
}

您可能知道所有的含义(或多或少)。然而,你的软件不知道,除非你将它们精确编码到这些字符串名。虽然我们有可能选择了相同的键(我们!),但我们更有可能没有。我们可能会修复我们的

1
:coffee:

也不是不一样,谁知道。

现在,我们需要写一些代码来将我的 JSON 转换

first_name

键到你的

firstName

键或转换成

first

它在你的

name

对象中或在

given

中或任何其他)。然后,我们需要再次这样做,当我们发现另一个人的信息使用不同的键名的人。冲洗。重复。

如果我们可以匹配 first_namename.firstgiven 甚至是混淆的 sadfeqpoif 键到一个让每个这些“含义到同样的”有意义的事情上,它也可以做到!

参考JSON-LD !JSON- ld表示“链接数据的JSON(JSON for Linked Data)”。它是一种将上下文化的含义编码到没有意义的JSON文档中的规范。

[推特 " " JSON- ld是一个将意义编码到无意义JSON文档的规范" “]

这里有一个例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "@context": {
    "@vocab": "http://schema.org/",
    "first_name": "givenName",
    "last_name": "familyName",
    "alias": "alternateName",
    "email": "email"
  },
  "first_name": "Benjamin",
  "last_name": "Young",
  "alias": "BigBlueHat",
  "email": "byoung@bigbluehat.com"
}

在这个之前 JSON 文档的升级 版本中,您会看到一个新的 @context 对象。这个对象处理从我的专用术语到 Schema.org 词汇的映射,该词汇表是通过值 @vocab引用的。

@vocab 下面的键/值对,键直接映射在有意义的URL http://schema.org/ 中。现在,我的键就有意义的。现在每个键有:

  • URL形式的标识

    例如,现在 last_name 映射到 http://schema.org/familyName

  • 通过访问 URL 找到 familyName定义:家庭的姓,在美国,一个人的最后一个名字。这可以和取的名字一起使用来取代姓名属性。"Family name. In the U.S., the last name of a Person. This can be used along with givenName instead of the name property.

很方便,不是吗?现在。如果您找到了那个JSON——或者从我的API中得到了它——您可以通过查找它们的url来了解键的含义。

映射到意义

了解JSON文档中的键意味着什么已经是对现状的一次升级,但我们不能就此止步!

下面是一个直接使用 Schema.org 术语(键名)的同样的 JSON 的示例:

1
2
3
4
5
6
7
{
  "@context": "http://schema.org/",
  "givenName": "Benjamin",
  "familyName": "Young",
  "alternateName": "BigBlueHat",
  "email": "byoung@bigbluehat.com"
}

这个 Schema.org JSON 示例和之前的版本有着同样的意义。在两个例子中,“BigBlueHat" 意味着是 本杰明•琼斯的别名(alternateName)

在 JSON-LD的世界中,我的带有 first_name 的 JSON 和这个带有 givenName 的JSON是等价的,完全相同的!

为 @context 编码

我们有两个不同的 JSON 文档,它们的含义是相同的。难以置信。然而,大多数用于处理这些的 JSON 代码看起来可能有占像这样:

1
2
3
var doc = {...}; // one of the documents above
var first = doc.first_name || doc.givenName;
console.log('First Name', first);

@context 对象如何帮助解决这个问题?我们还是在找字符串,而不是东西。

我们有两个选项:

  • 扩展(expansion)
  • 压缩(compaction)

Expansion

281/5000 在JSON-LD的术语中,扩展意味着取那些有意义的、基于url的名称,并使它们(至少在处理代码中)实际上是url。下面是对上面两个上下文化的例子所做的扩展过程的输出(记住!他们现在相同了!):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[
  {
    "http://schema.org/alternateName": [
      {
        "@value": "BigBlueHat"
      }
    ],
    "http://schema.org/email": [
      {
        "@value": "byoung@bigbluehat.com"
      }
    ],
    "http://schema.org/givenName": [
      {
        "@value": "Benjamin"
      }
    ],
    "http://schema.org/familyName": [
      {
        "@value": "Young"
      }
    ]
  }
]

以上是上述两个文档在JSON-LD处理系统中的内部相同表示。它的结构是这样的,因为有更多的数据可以(在实际的例子中,很可能会)添加到这些数组、对象等中。

但是,您目前可能还没有得到将所有JSON处理代码切换为使用此格式的灵感。别担心!这就是压缩的原因!

Compaction

JSON-LD处理器将采用上述两个示例文档,将它们转换为扩展格式,然后(如果要求)将它们转换为更人性化的压缩变体,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "@context": {
    "@vocab": "http://schema.org/",
    "first_name": "givenName",
    "last_name": "familyName",
    "alias": "alternateName",
    "email": "email"
  },
  "alias": "BigBlueHat",
  "email": "byoung@bigbluehat.com",
  "last_name": "Young",
  "first_name": "Benjamin"
}

这不是对第一个示例的复制/粘贴,可以在 JSON-LD Playground 上试一下。

代码

这儿是一个使用 json-ld.js 的魔法:

1
2
3
4
5
var recontextualized = {};
jsonld.compact(schema_org_doc, my_context,
  function(err, compacted) {
    recontextualized = compacted;
  });

运行该代码,可以看到上面的输出,这个魔法没有更多的代码。

下面是一个包含此示例内容的实时环境,因此您可以对其进行测试:

JS Bin on jsbin.com

http://static.jsbin.com/js/embed.min.js?3.35.11

有意义的个人API

现在我们可以将一个简单的 JSON 文档转换为有神奇意义的 JSON-LD 文档,让我们看看这在实践中给我们带来了什么。

让我们看看来自三个不同的人员信息提供者(通常称为社交网络)的三个JSON API端点:

  • Meetup
  • Twitter
  • LinkedIn

首先,这里是完整的特殊 @context,以涵盖我希望从这三个社交网络“规范化”的内容:

1
2
3
4
5
6
7
8
9
{
  "@vocab": "http://schema.org/",
  "first_name": "givenName",
  "last_name": "familyName",
  "alias": "alternateName",
  "job_title": "jobTitle",
  "city": "addressLocality",
  "country": "addressCountry"
}

下面是URLs,旧的JSON文本和站点定制的 @context ,需要称映射到Schema.org标准URL。还有一个JSBIN工作区,您可以为每个工作区fork一个!

Meetup

URL:

https://api.meetup.com/2/member/19524571?&sign=true&photo-host=public&page=20&only=country,city,link,bio,name

JSON:

1
2
3
4
5
6
7
{
  "link": "http://www.meetup.com/members/19524571",
  "name": "Benjamin Young",
  "country": "us",
  "bio": "aka BigBlueHat -=- Developer, Web, & Open Source Advocate, Invited Expert in the Annotation and Digital Publishing Working Groups at the W3C. Previously an inventor and evangelist for IBM's Cloudant, Couchbase, and also CTO at InnoVenture.",
  "city": "Greenville"
}

Meetup映射 @context

1
2
3
4
5
6
{
  "city": "http://schema.org/addressLocality",
  "country": "http://schema.org/addressCountry",
  "bio": "http://schema.org/description",
  "name": "http://schema.org/name"
}

JS Bin on jsbin.com

http://static.jsbin.com/js/embed.min.js?3.35.11

Twitter

URL: https://api.twitter.com/1.1/users/show.json?screen_name=bigbluehat&user_id=15841047

JSON (只有部分结果在下面因为它是巨大的!)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "id": 15841047,
  "id_str": "15841047",
  "name": "bigbluehat",
  "screen_name": "bigbluehat",
  "location": "Greenville, SC",
  "profile_location": null,
  "description": "inventor & evangelist - I :heart: hypothes.is, @couchdb, open source, open communities. I organize @RESTFest & @OpenUpstate."
  ...
}

Twitter 映射@context:

1
2
3
4
5
6
{
  "name": "http://schema.org/alternateName",
  "screen_name": "http://schema.org/alternateName",
  "description": "http://schema.org/description",
  "location": "http://schema.org/location"
}

JS Bin on jsbin.com

http://static.jsbin.com/js/embed.min.js?3.35.11

LinkedIn

URL:https://api.linkedin.com/v1/people/~?format=json

JSON:

1
2
3
4
5
6
7
8
9
{
  "firstName": "Benjamin",
  "headline": "Invited Expert, W3C",
  "id": "Ol-pwbI97V",
  "lastName": "Young",
  "siteStandardProfileRequest": {
    "url": "https://www.linkedin.com/profile/view?id=AAoAAAAAx4oBxNlPOjsrspSms5FMi7Tx0c-EBSk&authType=name&authToken=XFwT&trk=api*a3227641*s3301901*"
  }
}

LinkedIn 映射 @context:

1
2
3
4
5
{
  "firstName": "http://schema.org/givenName",
  "lastName": "http://schema.org/familyName",
  "headline": "http://schema.org/jobTitle"
}

JS Bin on jsbin.com

http://static.jsbin.com/js/embed.min.js?3.35.11

结论

我现在有三个不同但有对我自己来说有意义的正常化文本。根据我使用的数据存储,我可以用多种方式合并这些数据。现在键都匹配了。当所有的标识符都在文档中有意义的时候,它们就有意义了。这远远超过了我从原始 GET 请求中得到的