1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

reqwestでWebAPIにアクセスする際にハマったこと

Posted at

環境

  • Ubuntu 22.04 LTS
  • rustup 1.25.1
  • cargo 1.62.1

課題

Rust で WebAPI にアクセスしたい。
reqwest というクレートを使うと HTTP 通信をして返ってきた JSON データを構造体に落とし込むまでをやってくれるのだが、
返ってきた JSON のプロパティ名と自分で定義した構造体のフィールド名は一致していないといけない。つまり、

WebAPI
{
	"userName": "田中一郎",
	"userAge": 30,
	"isPremiumMember": false
}

のような会員情報の JSON を返す WebAPI にアクセスするとき、

Rust
struct Member {
	userName: String,
	userAge: i32,
	isPremiumMember: bool
}

のような構造体を定義しておく必要があるのだが、これを実行すると

warning: structure field `userName` should have a snake case name
warning: structure field `userAge` should have a snake case name
warning: structure field `isPremiumMember` should have a snake case name

のように怒られる。Rust の構造体のフィールド名はスネークケースで書くべきだからだ。
このままでも一応コードは動くけれど、大量の warning が出たままなのは気持ちが悪い。
どうすればよいだろうか?

ソースコード

Cargo.tomlはこんな感じ。
reqwest クレートで JSON を構造体に変換するために features に json を指定している。
serde クレートは構造体の直列化に、tokio クレートは非同期処理のために使用している。

[package]
name = "reqwest-sample"
version = "0.1.0"
edition = "2021"

[dependencies]
reqwest = { version = "0.11.11", features = ["json"] }
serde = { version = "1.0.140", features = ["derive"] }
tokio = { version = "1.20.0", features = ["full"] }

src/main.rsはこんな感じ。
まず JSON を受け取るための構造体を定義し、impl Member 以下で reqwest を使って WebAPI にアクセスする関数を定義している。

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
struct Member {
    userName: String,
    userAge: i32,
    isPremiumMember: bool
}

impl Member {
    async fn get(url: &str) -> Result<Self, reqwest::Error> {
        let response = reqwest::get(url).await.unwrap();
        let json = response.json::<Self>().await.unwrap();
        Ok(json)
    }
}

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let url = "https://gist.githubusercontent.com/YUUKIToriyama/1045d7941e4e9dc1c416b120050c8f1b/raw/213f8c713860b4b7ec90ff15f420e1e96826cc84/sample.json";
    let member = Member::get(url).await.unwrap();
    println!("{:#?}", member);
    Ok(())
}

解決法

serde のアトリビュートを使えば、JSON のプロパティをそれと異なる名前のフィールドに格納できることがわかった( https://serde.rs/field-attrs.html#alias )。

たとえば、次のようにすれば良い。

struct Member {
    #[serde(alias = "userName")]
    user_name: String,
    #[serde(alias = "userAge")]
    user_age: i32,
    #[serde(alias = "isPremiumNumber")]
    is_premium_member: bool
}

応用

serde のアトリビュートを使えば、異なる名前のプロパティとフィールドを紐付けられることがわかった。
これを応用すれば、次のようなプロパティ名が日本語の JSON をうまく処理することができる。

WebAPI
{
	"名前": "田中一郎",
	"年齢": 30,
	"有料会員": false
}
Rust
struct Member {
    #[serde(alias = "名前")]
    user_name: String,
    #[serde(alias = "年齢")]
    user_age: i32,
    #[serde(alias = "有料会員")]
    is_premium_member: bool
}

どうやら最新の Rust では識別子に Ascii 以外の文字も使えるらしいので、この場合そのままでもいいかもしれませんが。

参考リンク

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?