使用在线工具jsonschema2pojo根据json生成java对象

如果你还在自己手动写model对象,那你就out了。

场景:使用retrofit请求github的api,要使返回的数据自动解析到java对象中你必须准备好一个和json完全对应的java对象。而github的一个Repository有很多字段,如这个请求:

https://api.github.com/repos/octocat/Hello-World

你可以直接打开这个链接查看返回的json数据。可以看到里面有很多数据项,如果我们手动写这个java对象将非常耗费时间。

所幸现在有了转换的工具。

jsonschema2pojo

jsonschema2pojo  是一个根据json转换成java对象的开源项目,只要把你的json字符串复制到相应的输入框中就能自动将其转换成java对象。它的强大之处在于,能解析列表式的json数据,把嵌套在内层的对象也解析出来。

先以上面的api请求为例,得到的json如下,这是一个单一的Repository数据:

{
  "id": 1296269,
  "name": "Hello-World",
  "full_name": "octocat/Hello-World",
  "owner": {
    "login": "octocat",
    "id": 583231,
    "avatar_url": "https://avatars.githubusercontent.com/u/583231?v=3",
    "gravatar_id": "",
    "url": "https://api.github.com/users/octocat",
    "html_url": "https://github.com/octocat",
    "followers_url": "https://api.github.com/users/octocat/followers",
    "following_url": "https://api.github.com/users/octocat/following{/other_user}",
    "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
    "organizations_url": "https://api.github.com/users/octocat/orgs",
    "repos_url": "https://api.github.com/users/octocat/repos",
    "events_url": "https://api.github.com/users/octocat/events{/privacy}",
    "received_events_url": "https://api.github.com/users/octocat/received_events",
    "type": "User",
    "site_admin": false
  },
  "private": false,
  "html_url": "https://github.com/octocat/Hello-World",
  "description": "This your first repo!",
  "fork": false,
  "url": "https://api.github.com/repos/octocat/Hello-World",
  "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks",
  "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}",
  "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}",
  "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams",
  "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks",
  "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}",
  "events_url": "https://api.github.com/repos/octocat/Hello-World/events",
  "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}",
  "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}",
  "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags",
  "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}",
  "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}",
  "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}",
  "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}",
  "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}",
  "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages",
  "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers",
  "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors",
  "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers",
  "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription",
  "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}",
  "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}",
  "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}",
  "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}",
  "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}",
  "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}",
  "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges",
  "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}",
  "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads",
  "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}",
  "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}",
  "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}",
  "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}",
  "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}",
  "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}",
  "created_at": "2011-01-26T19:01:12Z",
  "updated_at": "2015-10-08T07:23:02Z",
  "pushed_at": "2015-10-02T01:04:21Z",
  "git_url": "git://github.com/octocat/Hello-World.git",
  "ssh_url": "[email protected]:octocat/Hello-World.git",
  "clone_url": "https://github.com/octocat/Hello-World.git",
  "svn_url": "https://github.com/octocat/Hello-World",
  "homepage": "",
  "size": 566,
  "stargazers_count": 1402,
  "watchers_count": 1402,
  "language": null,
  "has_issues": true,
  "has_downloads": true,
  "has_wiki": true,
  "has_pages": false,
  "forks_count": 1110,
  "mirror_url": null,
  "open_issues_count": 139,
  "forks": 1110,
  "open_issues": 139,
  "watchers": 1402,
  "default_branch": "master",
  "network_count": 1110,
  "subscribers_count": 1747
}

其中有一个数据项比较特殊,那就是owner(在第5行),它的值也是一个对象。我们把这个json放在http://www.jsonschema2pojo.org/ 网站左边的输入框中,右边的设置请按照图里的来。 

blob.png

其中右边设置的Class name一项默认是Example,表示生成的对象名(最外层)就是Example,因为它无法从json中得知这到底是什么对象。我们这里期望得到的对象名是Repository,可以改成Repository就可以了,或者这里不改,在生成之后的java对象里改。

注意里面还有个owner,它本身也是对象,jsonschema2pojo能把这个对象也解析出来,而这个对象的类名耶就是Owner。

我们点击Preview按钮,在弹出的对话框中生成了两个类:

blob.png

第一个就是Owner,第二个截图没有显示出来,是Repository类(我把Class name改成了Repository)。

而这里的@Generated("org.jsonschema2pojo")可以直接删掉。

可以想象如此之多的变量如果自己写要花费多长时间。

不过需要注意的是jsonschema2pojo对json字符串的总长度有要求。这样如果你像比如github请求的是一个列表数据比如,那么你得到的json很可能超过长度限制。

比如我们使用github的搜索api发出一个这样的请求:

https://api.github.com/search/repositories?q=tetris+language:assembly&sort=starsℴ=desc

 blob.png

返回的json数据首先打印的是列表长度total_count,这里是297。这是搜索结果的总长度,但是其实这次请求返回的只有30 条,因为这是第一页的数据。但是30条已经很长了,超过了长度限制。

要把这样的json转换成对象必须得找个搜索结果比较短的,我们换成

https://api.github.com/search/repositories?q=codebox+language:java&sort=starsℴ=desc

 这个返回的结果只有9项,虽然也很长但是还是没有超过限制。

blob.png

直接把生成的结果复制到jsonschema2pojo的左侧输入框,把类名改成Repositories,表示这是一个数据集合。

生成的类结构从前面部分就能看出来了,分别是Repositories,Item以及Owner,其中Item对应的就是刚刚我们的Repository类,这里它只能根据json猜测这个类名是Item。 

我们可以生成之后把Item改成Repository就行了。 

我之所以再举一个搜索结果为列表的例子是为了验证一个列表式的json是否可以直接转换成java类。

注意

以上只是生成普通的对象,但是如果想让生成的类和retrofit一起使用并直接返回解析好了的Repositories对象,里面的类名保留为默认的才行。而且retrofit默认使用的是GsonConverter,因此在解析之前,最好把Annotation style设置成Gson。

blob.png

使用这种方式生成的java类会用注解的方式把json中的字段和类中的字段对应:

@SerializedName("full_name")
@Expose
private String fullName;

这表示在json中字段名为full_name而在类中变量名为fullName。

这两个注解是gson的因此

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

总结

可以看到jsonschema2pojo的实用性非常高。