🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

1回答

1587閲覧

serdeでYYYY-MM-DD形式のJSON文字列をNaiveDateにデシリアライズしたい

tomlla_92

総合スコア11

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

0グッド

0クリップ

投稿2021/03/15 04:53

編集2021/03/15 09:28

やりたいこと

YYYY-MM-DD 形式のjson文字列をNaiveDateに変換したい

rust

1let json_str = r#" 2{ 3 "id": 1, 4 "date": "2021-03-15" 5}"#;

rust

1#[derive(Serialize, Deserialize, Debug)] 2struct Record { 3 id: i32, 4 #[serde(with="yyyy_mm_dd_fmt")] 5 date: NaiveDate, 6}

現状のコード全体

rust

1mod yyyy_mm_dd_fmt { 2 use chrono::{NaiveDate}; 3 use serde::{self, Deserialize, Serializer, Deserializer}; 4 const FORMAT: &'static str = "%Y-%m-%d"; 5 6 pub fn serialize<S>(date: &NaiveDate, serializer: S) -> Result<S::Ok, S::Error> 7 where S: Serializer, 8 { 9 let s = format!("{}", date.format(FORMAT)); 10 serializer.serialize_str(&s) 11 } 12 13 pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveDate, D::Error> 14 where D: Deserializer<'de>, 15 { 16 let s = String::deserialize(deserializer)?; 17 let nd = NaiveDate::parse_from_str(&s, FORMAT); 18 nd 19 } 20} 21 22use serde::{Serialize, Deserialize}; 23use chrono::NaiveDate; 24 25#[derive(Serialize, Deserialize, Debug)] 26struct Record { 27 id: i32, 28 #[serde(with="yyyy_mm_dd_fmt")] 29 date: NaiveDate, 30} 31 32fn main() { 33 let json_str = r#" 34 { 35 id:1, 36 date:"2021-03-15", 37 }"#; 38 let record: Record = serde_json::from_str(json_str).unwrap(); 39 println!("{:?}", record) 40}

きになっているところ

rust

1 pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveDate, D::Error> 2 where D: Deserializer<'de>, 3 { 4 let s = String::deserialize(deserializer)?; 5 let nd = NaiveDate::parse_from_str(&s, FORMAT); 6 nd 7 }

↑ 返り値の型が違うのでここでこのまま返せないのはわかっているんですが、matchなりmap_errを使ったあとに、
どうやって chrono::ParseError から Deserializer<'de> オブジェクトを作ればよいのでしょう?

cargo build の結果

| 15 | pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveDate, D::Error> | --------------------------- expected `std::result::Result<NaiveDate, <D as _::_serde::Deserializer<'de>>::Error>` because of return type ... 20 | nd | ^^ expected associated type, found struct `chrono::ParseError` | = note: expected enum `std::result::Result<_, <D as _::_serde::Deserializer<'de>>::Error>` found enum `std::result::Result<_, chrono::ParseError>` help: consider constraining the associated type `<D as _::_serde::Deserializer<'de>>::Error` to `chrono::ParseError` | 16 | where D: Deserializer<'de, Error = chrono::ParseError>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

こんにちは。まず確認したいのですが環境は以下であっていますか

Rustのバージョン: 1.50.0
依存クレート

toml

1[dependencies] 2serde = { version = "1.0.117", features = ["derive"]} 3serde_json = "1.0.59" 4chrono = { version = "0.4.19", features = ["serde"] }

この設定であれば NaiveDate でそのまま YYYY-MM-DD をデシリアライズできます。

toml

1use chrono::NaiveDate; 2use serde::{Deserialize, Serialize}; 3 4#[derive(Serialize, Deserialize, Debug)] 5struct Record { 6 id: i32, 7 date: NaiveDate, 8} 9 10fn main() { 11 // JSONフォーマットにエラーがあったので修正した 12 let json_str = r#" 13 { 14 "id":1, 15 "date":"2021-03-15" 16 }"#; 17 let record: Record = serde_json::from_str(json_str).unwrap(); 18 println!("{:?}", record) 19} 20

console

1$ cargo run 2 Finished dev [unoptimized + debuginfo] target(s) in 0.01s 3 Running `target/debug/serde_chrono` 4Record { id: 1, date: 2021-03-15 }

質問が「どうやって chrono::ParseError から Deserializer<'de> オブジェクトを作ればよいのでしょう」だったのでそれにもお答えします。

まず、作らないといけないのは Deserializer<'de> ではなく <D as _::_serde::Deserializer<'de>>::Error です。 <T as Trait>::Assoc という記法は見慣れないかもしれませんが、 T 型が実装している Trait における関連アイテムの Assoc を指す記法です。 つまりここでは Deserializer の関連型の Error を指しています。そこで Deserializer のドキュメントを見ると Errorde::Error トレイト を実装していることが分かります。なので de::Error にある関連関数を使って Error を作ることになります。実際のコードは以下のようになりました。

rust

1mod yyyy_mm_dd_fmt { 2 use chrono::NaiveDate; 3 // `de` をインポート 4 use serde::{self, de, Deserialize, Deserializer, Serializer}; 5 const FORMAT: &'static str = "%Y-%m-%d"; 6 7 pub fn serialize<S>(date: &NaiveDate, serializer: S) -> Result<S::Ok, S::Error> 8 where 9 S: Serializer, 10 { 11 let s = format!("{}", date.format(FORMAT)); 12 serializer.serialize_str(&s) 13 } 14 15 pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveDate, D::Error> 16 where 17 D: Deserializer<'de>, 18 { 19 let s = String::deserialize(deserializer)?; 20 NaiveDate::parse_from_str(&s, FORMAT) 21 // `de::Error::custom` でエラー型の値を作る 22 .map_err(|e| de::Error::custom(format!("chrono returned an error: {}", e))) 23 } 24}

こちらでも同様にデシリアライズできました。

console

1$ cargo run 2 Finished dev [unoptimized + debuginfo] target(s) in 0.01s 3 Running `target/debug/serde_chrono` 4Record { id: 1, date: 2021-03-15 }

以上で解決できるのではないかと思いますがどうでしょうか。分からない用語などありましたら遠慮なく質問下さい。

投稿2021/03/15 08:22

blackenedgold

総合スコア468

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tomlla_92

2021/03/15 10:09 編集

非常に丁寧に解説いただきありがとうございました! まずこの質問を試した際、chrono で `features = ["serde"]` を指定しておらず、 単純にこう書いていました。 ``` chrono = "0.4" ``` features = ["serde"]` に変更して `#[serde(with="yyyy_mm_dd_fmt")]` を外すと問題なく動作しました。 ひとまずやりたいことは達成できました。 また実際に今後独自フォーマットのSerialize/Deserializeが必要になった際も提示していただいたサンプルコードのようにエラーハンドリングを行えばやりたい動作が実装できそうです。 ただし、今時点の私のRustへの理解レベルでは今後似たような問題にぶつかった際に > そこで Deserializer のドキュメントを見ると Error は de::Error トレイト を実装していることが分かります。 > なので de::Error にある関連関数を使って Error を作ることになります。 という点をやコードやドキュメントから読み解けなさそうです。 この点はもうすこしRustのトレイト関連のドキュメント/書籍などを読んで理解してみようと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問