serde-rs / serde

Serialization framework for Rust
https://serde.rs/
Apache License 2.0
8.82k stars 748 forks source link

human_readable flag does not get carried through #2704

Open stroberrysugar opened 4 months ago

stroberrysugar commented 4 months ago

The issue I have is related to serializing chrono's DateTime objects differently based on the human_readable flag. If human_readable is set to true, I need it to be serialized as an ISO string. If it's false, it needs to be serialized as a Bson DateTime object.

However, serde seems to be ignoring the human_readable flag in my code (or the flag isn't being passed through).

I eventually narrowed down the issue to this (in a different project for an minimal, reproducible example).

To do my experiment, I have a struct that serializes to this:

{
  "field1": 0,
  "field2": 0,
  "human_readable": true
}

This JSON output will let me know what the value of the human_readable flag is.

Here's the Rust code that does this:

use bson::SerializerOptions;
use serde::{ser::SerializeStruct, Serialize};

#[derive(Default, Serialize)]
struct Main {
    field1: usize,
    field2: usize,
    #[serde(flatten)]
    serializer_info: SerializerInfo,
}

#[derive(Default)]
struct SerializerInfo;

impl Serialize for SerializerInfo {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let human_readable = serializer.is_human_readable();

        let mut serializer_info = serializer.serialize_struct("SerializerInfo", 1)?;

        serializer_info.serialize_field("human_readable", &human_readable)?;

        serializer_info.end()
    }
}

fn main() {
    let document = bson::to_document_with_options(
        &Main::default(),
        SerializerOptions::builder().human_readable(false).build(),
    )
    .unwrap();

    println!("{}", serde_json::to_string_pretty(&document).unwrap());
}

This code always responds with a JSON output that has human_readable set to true, despite me setting it to false in my bson serializer.

What's interesting is that this problem disappears when I remove the #[serde(flatten)] attribute. This is what I get when I do that:

{
  "field1": 0,
  "field2": 0,
  "serializer_info": {
    "human_readable": false
  }
}

Do you think this is a problem with bson or serde?

Mingun commented 4 months ago

This is problem with is_human_readable() flag which is not preserved in internal deserializers. I tried to fix that in #1919, but the PR was closed as won't fix.

survived commented 3 months ago

I reproduced the same problem while using ciborium (which is always supposed to be human_readable = false) and #[serde(flatten)]. Things inside of #[serde(flatten)] have human_readable = true. I followed the code in cargo expand, and the issue basically comes down to calling FlatMapSerializer/FlatMapDeserializer which don't implement fn is_human_readable() and it defaults to true.

I think both this structs could accept is_human_readable flag as input and address this issue. #1919 should have done the trick, it's very strange to see it being rejected.

plusls commented 3 weeks ago

any progress?

bdbai commented 3 weeks ago

Possibly duplicate: #2172