使用Serde反序列化嵌套JSON结构时,“无效类型:映射,预期为序列”

zep*_*zep 5 serialization json rust serde serde-json

我正在尝试轮询GitHub API中的问题并打印出来。为此,我需要反序列化从cURL GET请求中收到的嵌套JSON结构。

我正在尝试获取数组中url所有对象的items

{
 "total_count": 4905,
 "incomplete_results": false,
 "items": [
    {
     "url": "https://api.github.com/repos/servo/saltfs/issues/789",
     "repository_url": "https://api.github.com/repos/servo/saltfs",
     "labels_url": 
   "https://api.github.com/repos/servo/saltfs/issues/789/labels{/name}",
  "comments_url": "https://api.github.com/repos/servo/saltfs/issues/789/comments",
  "events_url": "https://api.github.com/repos/servo/saltfs/issues/789/events",
  "html_url": "https://github.com/servo/saltfs/issues/789",
  "id": 293260512,
  "number": 789,
  "title": "Stop setting $CARGO_HOME to its default value",
  "user": {
    "login": "SimonSapin",
    "id": 291359,
    "avatar_url": "https://avatars0.githubusercontent.com/u/291359?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/SimonSapin",
    "html_url": "https://github.com/SimonSapin",
    "followers_url": "https://api.github.com/users/SimonSapin/followers",
    "following_url": "https://api.github.com/users/SimonSapin/following{/other_user}",
    "gists_url": "https://api.github.com/users/SimonSapin/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/SimonSapin/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/SimonSapin/subscriptions",
    "organizations_url": "https://api.github.com/users/SimonSapin/orgs",
    "repos_url": "https://api.github.com/users/SimonSapin/repos",
    "events_url": "https://api.github.com/users/SimonSapin/events{/privacy}",
    "received_events_url": "https://api.github.com/users/SimonSapin/received_events",
    "type": "User",
    "site_admin": false
  },
  "labels": [
    {
      "id": 341722396,
      "url": "https://api.github.com/repos/servo/saltfs/labels/E-easy",
      "name": "E-easy",
      "color": "02e10c",
      "default": false
    }
  ],
  "state": "open",
  "locked": false,
  "assignee": null,
  "assignees": [

  ],
  "milestone": null,
  "comments": 0,
  "created_at": "2018-01-31T18:16:09Z",
  "updated_at": "2018-01-31T18:16:49Z",
  "closed_at": null,
  "author_association": "MEMBER",
  "body": "In `buildbot/master/files/config/environments.py` we set `CARGO_HOME` to Cargo’s default value. Now that `mach` does not set it (since https://github.com/servo/servo/pull/19395), this has no effect. We can remove these lines.",
  "score": 1.0
},
{
  "url": "https://api.github.com/repos/servo/servo/issues/19916",
  "repository_url": "https://api.github.com/repos/servo/servo",
  "labels_url": "https://api.github.com/repos/servo/servo/issues/19916/labels{/name}",
  "comments_url": "https://api.github.com/repos/servo/servo/issues/19916/comments",
  "events_url": "https://api.github.com/repos/servo/servo/issues/19916/events",
  "html_url": "https://github.com/servo/servo/issues/19916",
  "id": 293237180,
  "number": 19916,
  "title": "Use a macro to create null-terminated C strings",
  "user": {
    "login": "jdm",
    "id": 27658,
    "avatar_url": "https://avatars1.githubusercontent.com/u/27658?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/jdm",
    "html_url": "https://github.com/jdm",
    "followers_url": "https://api.github.com/users/jdm/followers",
    "following_url": "https://api.github.com/users/jdm/following{/other_user}",
    "gists_url": "https://api.github.com/users/jdm/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/jdm/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/jdm/subscriptions",
    "organizations_url": "https://api.github.com/users/jdm/orgs",
    "repos_url": "https://api.github.com/users/jdm/repos",
    "events_url": "https://api.github.com/users/jdm/events{/privacy}",
    "received_events_url": "https://api.github.com/users/jdm/received_events",
    "type": "User",
    "site_admin": false
  },
  "labels": [
    {
      "id": 89384911,
      "url": "https://api.github.com/repos/servo/servo/labels/C-assigned",
      "name": "C-assigned",
      "color": "02d7e1",
      "default": false
    },
    {
      "id": 15997664,
      "url": "https://api.github.com/repos/servo/servo/labels/E-easy",
      "name": "E-easy",
      "color": "02e10c",
      "default": false
    },
    {
      "id": 135307111,
      "url": "https://api.github.com/repos/servo/servo/labels/I-cleanup",
      "name": "I-cleanup",
      "color": "e11d21",
      "default": false
    }
  ],
  "state": "open",
  "locked": false,
  "assignee": null,
  "assignees": [

  ],
  "milestone": null,
  "comments": 3,
  "created_at": "2018-01-31T17:04:06Z",
  "updated_at": "2018-01-31T22:03:56Z",
  "closed_at": null,
  "author_association": "MEMBER",
  "body": "When we write them by hand (eg. `b\"some string\\0\"`), we invariably get them wrong in ways that are tricky to notice (https://github.com/servo/servo/pull/19915). We should use a macro like this instead:\r\n```rust\r\nmacro_rules! c_str {\r\n    ($str:expr) => {\r\n        concat!($str, \"\\0\").as_bytes()\r\n    }\r\n}\r\n```\r\nThis would allow us to write code like `(c_str!(\"PEParseDeclarationDeclExpected\"), Action::Skip)` instead of https://github.com/emilio/servo/blob/d82c54bd3033cc3277ebeb4854739bebe4e20f2f/ports/geckolib/error_reporter.rs#L237. We should be able to clean up all of the uses in that file.\r\n\r\nNo need to run any automated tests; if it builds with `./mach build-geckolib`, then it's good enough for a pull request.",
  "score": 1.0
}
]}
Run Code Online (Sandbox Code Playgroud)

我的request函数发出一个cURL请求并接收上述JSON。然后,我使用serde_json反序列化JSON

extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

mod engine;
mod server;
use engine::request;
use std::string::String;
use self::serde_json::{Error, Value};

#[derive(Serialize, Deserialize)]
struct obj {
    items: Vec<String>,
}

fn main() {
    let output_jn: String = request(
        "https://api.github.com/search/issues?q=is:issue+label:e-easy",
    ).to_string(); //gets json structure as string
    let json: obj = serde_json::from_str(&output_jn).unwrap();

    for elem in json.iter() {
        println!("{:?}", elem);
    }
}
Run Code Online (Sandbox Code Playgroud)

我收到以下错误消息

{
 "total_count": 4905,
 "incomplete_results": false,
 "items": [
    {
     "url": "https://api.github.com/repos/servo/saltfs/issues/789",
     "repository_url": "https://api.github.com/repos/servo/saltfs",
     "labels_url": 
   "https://api.github.com/repos/servo/saltfs/issues/789/labels{/name}",
  "comments_url": "https://api.github.com/repos/servo/saltfs/issues/789/comments",
  "events_url": "https://api.github.com/repos/servo/saltfs/issues/789/events",
  "html_url": "https://github.com/servo/saltfs/issues/789",
  "id": 293260512,
  "number": 789,
  "title": "Stop setting $CARGO_HOME to its default value",
  "user": {
    "login": "SimonSapin",
    "id": 291359,
    "avatar_url": "https://avatars0.githubusercontent.com/u/291359?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/SimonSapin",
    "html_url": "https://github.com/SimonSapin",
    "followers_url": "https://api.github.com/users/SimonSapin/followers",
    "following_url": "https://api.github.com/users/SimonSapin/following{/other_user}",
    "gists_url": "https://api.github.com/users/SimonSapin/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/SimonSapin/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/SimonSapin/subscriptions",
    "organizations_url": "https://api.github.com/users/SimonSapin/orgs",
    "repos_url": "https://api.github.com/users/SimonSapin/repos",
    "events_url": "https://api.github.com/users/SimonSapin/events{/privacy}",
    "received_events_url": "https://api.github.com/users/SimonSapin/received_events",
    "type": "User",
    "site_admin": false
  },
  "labels": [
    {
      "id": 341722396,
      "url": "https://api.github.com/repos/servo/saltfs/labels/E-easy",
      "name": "E-easy",
      "color": "02e10c",
      "default": false
    }
  ],
  "state": "open",
  "locked": false,
  "assignee": null,
  "assignees": [

  ],
  "milestone": null,
  "comments": 0,
  "created_at": "2018-01-31T18:16:09Z",
  "updated_at": "2018-01-31T18:16:49Z",
  "closed_at": null,
  "author_association": "MEMBER",
  "body": "In `buildbot/master/files/config/environments.py` we set `CARGO_HOME` to Cargo’s default value. Now that `mach` does not set it (since https://github.com/servo/servo/pull/19395), this has no effect. We can remove these lines.",
  "score": 1.0
},
{
  "url": "https://api.github.com/repos/servo/servo/issues/19916",
  "repository_url": "https://api.github.com/repos/servo/servo",
  "labels_url": "https://api.github.com/repos/servo/servo/issues/19916/labels{/name}",
  "comments_url": "https://api.github.com/repos/servo/servo/issues/19916/comments",
  "events_url": "https://api.github.com/repos/servo/servo/issues/19916/events",
  "html_url": "https://github.com/servo/servo/issues/19916",
  "id": 293237180,
  "number": 19916,
  "title": "Use a macro to create null-terminated C strings",
  "user": {
    "login": "jdm",
    "id": 27658,
    "avatar_url": "https://avatars1.githubusercontent.com/u/27658?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/jdm",
    "html_url": "https://github.com/jdm",
    "followers_url": "https://api.github.com/users/jdm/followers",
    "following_url": "https://api.github.com/users/jdm/following{/other_user}",
    "gists_url": "https://api.github.com/users/jdm/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/jdm/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/jdm/subscriptions",
    "organizations_url": "https://api.github.com/users/jdm/orgs",
    "repos_url": "https://api.github.com/users/jdm/repos",
    "events_url": "https://api.github.com/users/jdm/events{/privacy}",
    "received_events_url": "https://api.github.com/users/jdm/received_events",
    "type": "User",
    "site_admin": false
  },
  "labels": [
    {
      "id": 89384911,
      "url": "https://api.github.com/repos/servo/servo/labels/C-assigned",
      "name": "C-assigned",
      "color": "02d7e1",
      "default": false
    },
    {
      "id": 15997664,
      "url": "https://api.github.com/repos/servo/servo/labels/E-easy",
      "name": "E-easy",
      "color": "02e10c",
      "default": false
    },
    {
      "id": 135307111,
      "url": "https://api.github.com/repos/servo/servo/labels/I-cleanup",
      "name": "I-cleanup",
      "color": "e11d21",
      "default": false
    }
  ],
  "state": "open",
  "locked": false,
  "assignee": null,
  "assignees": [

  ],
  "milestone": null,
  "comments": 3,
  "created_at": "2018-01-31T17:04:06Z",
  "updated_at": "2018-01-31T22:03:56Z",
  "closed_at": null,
  "author_association": "MEMBER",
  "body": "When we write them by hand (eg. `b\"some string\\0\"`), we invariably get them wrong in ways that are tricky to notice (https://github.com/servo/servo/pull/19915). We should use a macro like this instead:\r\n```rust\r\nmacro_rules! c_str {\r\n    ($str:expr) => {\r\n        concat!($str, \"\\0\").as_bytes()\r\n    }\r\n}\r\n```\r\nThis would allow us to write code like `(c_str!(\"PEParseDeclarationDeclExpected\"), Action::Skip)` instead of https://github.com/emilio/servo/blob/d82c54bd3033cc3277ebeb4854739bebe4e20f2f/ports/geckolib/error_reporter.rs#L237. We should be able to clean up all of the uses in that file.\r\n\r\nNo need to run any automated tests; if it builds with `./mach build-geckolib`, then it's good enough for a pull request.",
  "score": 1.0
}
]}
Run Code Online (Sandbox Code Playgroud)

我确定我在反序列化JSON结构时犯了一个愚蠢的错误,我已经尝试了许多排列和组合,但是我什么也做不了。

dto*_*nay 5

看一下您的JSON输入数据的这一部分:

{
  ...
  "items": [
    {
      ...
      "title": "Stop setting $CARGO_HOME to its default value",
      ...
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)
  • 顶级数据结构是JSON映射,因此在Rust中,它将表示为结构。我会用你的名字Obj
  • 顶级JSON映射具有一个称为的键,"items"因此在Rust中,这将是struct items内部的一个字段Obj
  • "items"映射中的值是JSON数组,因此在Rust中,我们使用Vec。
  • JSON数组中的每个元素都是一个JSON映射,因此在Rust中,我们需要一个结构。我们可以称之为Issue
  • 每个问题都有一个称为的JSON密钥,"title"因此这将是struct title内部的一个字段Issue
  • 的值"title"是JSON字符串,因此我们可以String在字段中使用Rust的类型。

#[derive(Deserialize, Debug)]
struct Obj {
    items: Vec<Issue>,
}

#[derive(Deserialize, Debug)]
struct Issue {
    title: String,
}

fn main() {
    let j = /* get the JSON data */;

    let issues = serde_json::from_str::<Obj>(j).unwrap();
    for i in issues.items {
        println!("{:#?}", i);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @til我的实验不能证明这一点,如果这里的顶级(外部)字典/映射中存在除“items”之外的键,即具有除“struct Issue”以外的值的键,其中在OP给出的示例中:“total_count”:某种整数,和“incomplete_results”:`bool`。然后我得到一个错误。然而,如果所有内容都被“结构”覆盖,则其他键将被忽略。 (2认同)