Skip to content
Allen edited this page May 28, 2020 · 1 revision

English | 中文

很多情况下,对于一个json输入源,我们只对其部分内容感兴趣。为了得到我们需要的小部分信息,去定义跟这个json串匹配的schema是一件麻烦的事件。对于体积庞大或者嵌套层次深的json串尤其如此。json-iterator提供了Any对象,可以很方便地从json串中获取你想要的元素,而不需要去定义schema

使用简单

假设我们有这么一个json

jsonStr := []byte(`{
  "users": [
    {
      "username": "system",
      "avatar_template": "/user_avatar/discourse.metabase.com/system/{size}/6_1.png",
      "id": -1
    },
    {
      "username": "zergot",
      "avatar_template": "https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png",
      "id": 89
    }
  ],
  "topics": {
    "can_create_topic": false,
    "more_topics_url": "/c/uncategorized/l/latest?page=1",
    "draft": null,
    "draft_key": "new_topic",
    "draft_sequence": null,
    "per_page": 30,
    "topics": [
      {
        "bumped": true,
        "id": 8,
        "excerpt": "Welcome to Metabase\u0026#39;s discussion forum. This is a place to get help on installation, setting up as well as sharing tips and tricks.",
        "category_id": 1,
        "unseen": false,
        "slug": "welcome-to-metabases-discussion-forum",
        "fancy_title": "Welcome to Metabase\u0026rsquo;s Discussion Forum",
        "bookmarked": null,
        "archived": false,
        "archetype": "regular",
        "highest_post_number": 1,
        "reply_count": 0,
        "visible": true,
        "closed": false,
        "liked": null,
        "posts_count": 1,
        "views": 197,
        "image_url": "/images/welcome/discourse-edit-post-animated.gif",
        "created_at": "2015-10-17T00:14:49.526Z",
        "last_posted_at": "2015-10-17T00:14:49.557Z",
        "pinned": true,
        "title": "Welcome to Metabase's Discussion Forum",
        "has_summary": false,
        "like_count": 0,
        "pinned_globally": true,
        "last_poster_username": "system",
        "posters": [
          {
            "extras": "latest single",
            "description": "Original Poster, Most Recent Poster",   // 我们需要这个
            "user_id": -1
          }
        ],
        "bumped_at": "2015-10-21T02:32:22.486Z",
        "unpinned": null
      }
    ]
  }
}`)

如果用传统的方法,那么首先我们应该先定义一个匹配这个json结构的结构体,然后调用Unmarshal来反序列化,再获取这个结构体中我们需要的字段的值。如果用Any,那么就很简单了:

any := jsoniter.Get(jsonStr, "topics", "topics", 0, "posters", 0, "description")
fmt.Println(any.ToString())
// Output:
// Original Poster, Most Recent Poster

只需要一行,我们就可以拿到我们想要的元素。然后调用Any对象提供的接口做下转换,就得到了我们要的description字符串

与schema结合

还是上面的例子

jsonStr := []byte(`{
  "users": [
    {
      "username": "system",
      "avatar_template": "/user_avatar/discourse.metabase.com/system/{size}/6_1.png",
      "id": -1
    },
    {
      "username": "zergot",
      "avatar_template": "https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png",
      "id": 89
    }
  ],
  "topics": {
    "can_create_topic": false,
    "more_topics_url": "/c/uncategorized/l/latest?page=1",
    "draft": null,
    "draft_key": "new_topic",
    "draft_sequence": null,
    "per_page": 30,
    "topics": [
      {
        "bumped": true,
        "id": 8,
        "excerpt": "Welcome to Metabase\u0026#39;s discussion forum. This is a place to get help on installation, setting up as well as sharing tips and tricks.",
        "category_id": 1,
        "unseen": false,
        "slug": "welcome-to-metabases-discussion-forum",
        "fancy_title": "Welcome to Metabase\u0026rsquo;s Discussion Forum",
        "bookmarked": null,
        "archived": false,
        "archetype": "regular",
        "highest_post_number": 1,
        "reply_count": 0,
        "visible": true,
        "closed": false,
        "liked": null,
        "posts_count": 1,
        "views": 197,
        "image_url": "/images/welcome/discourse-edit-post-animated.gif",
        "created_at": "2015-10-17T00:14:49.526Z",
        "last_posted_at": "2015-10-17T00:14:49.557Z",
        "pinned": true,
        "title": "Welcome to Metabase's Discussion Forum",
        "has_summary": false,
        "like_count": 0,
        "pinned_globally": true,
        "last_poster_username": "system",
        "posters": [
          {    // 这次我们需要这个
            "extras": "latest single",
            "description": "Original Poster, Most Recent Poster",   
            "user_id": -1
          }
        ],
        "bumped_at": "2015-10-21T02:32:22.486Z",
        "unpinned": null
      }
    ]
  }
}`)

这次我们需要"posters"数组的第一个结构体,我们现在已经有它的schema定义了,除此之外这个json的其他信息我都不需要,那么如何通过Any对象获得这个结构体呢?我们需要ToVal接口:

type Poster struct {
	Extras string `json:"extras"`
	Desc string `json:"description"`
	UserId int `json:"user_id"`
}

var p Poster
any := jsoniter.Get(jsonStr, "topics", "topics", 0, "posters", 0)
any.ToVal(&p)
fmt.Printf("extras=%s\ndescription=%s\nuser_id=%d\n", p.Extras, p.Desc, p.UserId)
// Output:
// extras=latest single
// description=Original Poster, Most Recent Poster
// user_id=-1

这里可以看到,首先我们拿到了"posters"第一个元素的Any对象,然后调用ToVal方法,就可以像之前的反序列化方法一样把数据解析出来。实际上,如果你的Any对象对应的是数组或对象类型的元素,它内部保存了这个元素原始的json串。当你需要获取其字段、元素或者将其反序列化出来的时候,才会触发解析。json-iterator内部将其称为懒解析。来看个数组的例子:

type User struct {
	UserName string `json:"username"`
	Template string `json:"avatar_template"`
	Id int `json:"id"`
}

var users []User
any := jsoniter.Get(jsonStr, "users")
fmt.Println(any.Get(0, "username").ToString())
// Output:
// system

any.ToVal(&users)
fmt.Printf("username=%s\navatar_template=%s\nid=%d\n", users[1].UserName, users[1].Template, users[1].Id)
// Output:
// username=zergot
// avatar_template=https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png
// id=89

数组元素的获取方法其实也是类似,这里不再详述。

有一点需要说明的是,只有数组和对象的json元素对应的Any才提供ToVal方法,也就是说这两种json元素的Any对象才实现了懒解析,其他诸如int,bool,string等都没有实现,实际上它们也不需要什么懒解析

Clone this wiki locally