简体   繁体   English

使用 Serde 反序列化嵌套的 JSON 结构时出现“无效类型:映射,需要序列”

[英]“invalid type: map, expected a sequence” when deserializing a nested JSON structure with Serde

I am trying to poll the GitHub API for issues and print them out.我正在尝试轮询 GitHub API 以查找问题并将其打印出来。 To do so, I need to deserialize a nested JSON structure that I receive from a cURL GET request.为此,我需要反序列化从 cURL GET 请求收到的嵌套 JSON 结构。

I am trying to get the url for all the objects in the items array:我正在尝试获取items数组中所有对象的url

{
 "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
}
]}

My request function makes a cURL request and receives the above JSON.我的request函数发出 cURL 请求并接收上述 JSON。 I then use serde_json to deserialize the JSON然后我使用 serde_json 反序列化 JSON

main.rs主文件

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);
    }
}

I get the following error message我收到以下错误消息

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: 
ErrorImpl { code: Message("invalid type: map, expected a sequence"), 
line: 1, column: 0 }', libcore/result.rs:945:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

I'm certain I'm making a stupid mistake in deserializing my JSON structure, I've tried a number of permutations and combinations but I couldn't get anything to work.我确信我在反序列化我的 JSON 结构时犯了一个愚蠢的错误,我已经尝试了许多排列和组合,但我无法得到任何工作。

Have a look at this part of your JSON input data:看看你的 JSON 输入数据的这一部分:

{
  ...
  "items": [
    {
      ...
      "title": "Stop setting $CARGO_HOME to its default value",
      ...
    }
  ]
}
  • The top-level data structure is a JSON map, so in Rust this will be represented as a struct.顶级数据结构是一个 JSON 映射,因此在 Rust 中这将表示为一个结构。 I will use your name Obj .我会用你的名字Obj
  • The top-level JSON map has a key called "items" so in Rust this will be a field items inside the Obj struct.顶级 JSON 映射有一个名为"items"的键,因此在 Rust 中,这将是Obj结构中的一个字段items
  • The value of "items" in the map is a JSON array, so in Rust let's use a Vec.地图中"items"的值是一个 JSON 数组,所以在 Rust 中让我们使用 Vec。
  • Each element in the JSON array is a JSON map so in Rust we need a struct for those. JSON 数组中的每个元素都是一个 JSON 映射,因此在 Rust 中我们需要一个结构体。 We can call it Issue .我们可以称之为Issue
  • Each issue has a JSON key called "title" so this will be a field title inside the Issue struct.每个问题都有一个名为"title"的 JSON 键,因此这将是Issue结构中的字段title
  • The value of "title" is a JSON string so we can use Rust's String type for the field. "title"的值是一个 JSON 字符串,因此我们可以使用 Rust 的String类型作为字段。

#[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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM