Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UtcDateTime derialize from json failed #81

Closed
Matrix-Zhang opened this issue Sep 12, 2017 · 4 comments
Closed

UtcDateTime derialize from json failed #81

Matrix-Zhang opened this issue Sep 12, 2017 · 4 comments
Labels

Comments

@Matrix-Zhang
Copy link

extern crate bson;
extern crate chrono;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

use bson::{Bson, to_bson, UtcDateTime};
use chrono::offset::Utc;
use serde_json::Value;

#[derive(Debug, Deserialize, Serialize)]
struct Foo {
    id: i32,
    datetime: UtcDateTime,
}

fn main() {
    let foo = Foo {
        id: 1,
        datetime: UtcDateTime(Utc::now()),
    };

    let document = to_bson(&foo).unwrap().as_document().unwrap().to_owned();

    println!("document: {:?}", document);

    let value: Value = Bson::from(document).into();
    println!("value: {:?}", value);

    let _: Foo = serde_json::from_value(value).unwrap();
}

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ErrorImpl { code: Message("invalid type: integer `1505192010346`, expected a signed integer"), line: 0, column: 0 }', /checkout/src/libcore/result.rs:906:4

@zonyitoo
Copy link
Contributor

I don't think it is possible to convert BSON extended JSON Value directly back to Foo.

Foo will be serialized to BSON as:

Document(OrderedDocument({"id": I32(1), "datetime": UtcDatetime(2017-09-12T14:15:00.234Z)}))

which is then converted to serde_json::Value as:

Object({"id": Number(PosInt(1)), "datetime": Object({"$date": Object({"$numberLong": Number(PosInt(1505225700234))})})})

in human readable form is:

{
    "id": 1,
    "datetime": {
        "$date": {
            "$numberLong": 1505225700234
        }
    }
}

As you can see, it is definitely not possible to be converted back to Foo.

What you actually want is Bson::from_json (or .into()).

let foo_bson: Bson = value.into();
let foo: Foo = bson::from_bson(foo_bson).unwrap();

So, this is not a bug. Won't fix.

@Stargateur
Copy link

Why in the world do you choice to deserialize to this ? How is this supposed to be usefull ? Could we envisage a better output format ? Like put as string, or directly put a the integer ?

@patrickfreed
Copy link
Contributor

@Stargateur The bson::DateTime (as it is now called) struct is used specifically as a convenience wrapper type for working with chrono::DateTimes in BSON. To see that in effect, consider the following:

let thing = Thing {
    bson_datetime: bson::DateTime(Utc::now()),
    chrono_datetime: Utc::now(),
};
println!("{:?}", bson::to_bson(&thing).unwrap());

This produces the following ouput:

Document(Document({"bson_datetime": DateTime(2020-07-17T23:22:18.653Z), "chrono_datetime": String("2020-07-17T23:22:18.653832701Z")}))

You can see that, because chrono_datetime relies on chrono::DateTime's Serialize implementation, it gets serialized it as a string. bson::DateTime, on the other hand, serializes as an actual BSON datetime, which is desired when working in BSON.

The JSON format you're seeing is called Extended JSON, and it is is a standardized way to serialize BSON types in JSON without losing the type information. When serializing to non-BSON formats, bson::DateTime produces the Extended JSON representation of the datetime because the type is supposed to be modelling a BSON datetime. To avoid getting this serialization output, you can use the chrono::DateTime in your struct, but that would make you lose the ability to deserialize it from BSON without some extra work. If you'd like to get the best of both worlds and deserialize from BSON datetimes and serialize using a non-extended JSON format, check out #191 for a workaround or RUST-506 to track work for making it easier to do this.

@zingi
Copy link

zingi commented Apr 2, 2021

With bson 1.2.0 there exist now the serde_helpers.

You can use it like:

// use serde::{Serialize, Deserialize};
// use bson::serde_helpers::chrono_datetime_as_bson_datetime;
#[derive(Serialize, Deserialize)]
struct Event {
    #[serde(with = "chrono_datetime_as_bson_datetime")]
    pub date: chrono::DateTime<chrono::Utc>,
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants