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

Add BoolFromInt adapter #462

Merged
merged 2 commits into from May 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 16 additions & 1 deletion serde_with/CHANGELOG.md
Expand Up @@ -24,7 +24,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

`time::OffsetDateTime` and `time::PrimitiveDateTime` can now be serialized with the `TimestampSeconds` and related converters.


```rust
// Rust
#[serde_as(as = "serde_with::TimestampMicroSecondsWithFrac<String>")]
Expand All @@ -49,6 +48,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
"rfc_3339": ["1997-11-21T09:55:06-06:00"],
```

* Deserialize `bool` from integers #456 462

Deserialize an integer and convert it into a `bool`.
`BoolFromInt<Strict>` (default) deserializes 0 to `false` and `1` to `true`, other numbers are errors.
`BoolFromInt<Flexible>` deserializes any non-zero as `true`.
Serialization only emits 0/1.

```rust
// Rust
#[serde_as(as = "BoolFromInt")] // BoolFromInt<Strict>
b: bool,

// JSON
"b": 1,
```

### Changed

* Bump MSRV to 1.53, since the new dependency `time` requires that version.
Expand Down
156 changes: 156 additions & 0 deletions serde_with/src/de/impls.rs
Expand Up @@ -1349,4 +1349,160 @@ impl<'de> DeserializeAs<'de, Cow<'de, [u8]>> for BorrowCow {
}
}

impl<'de> DeserializeAs<'de, bool> for BoolFromInt<Strict> {
fn deserialize_as<D>(deserializer: D) -> Result<bool, D::Error>
where
D: Deserializer<'de>,
{
struct U8Visitor;
impl<'de> Visitor<'de> for U8Visitor {
type Value = bool;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("an integer 0 or 1")
}

fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
where
E: Error,
{
match v {
0 => Ok(false),
1 => Ok(true),
unexp => Err(Error::invalid_value(
Unexpected::Unsigned(unexp as u64),
&"0 or 1",
)),
}
}

fn visit_i8<E>(self, v: i8) -> Result<Self::Value, E>
where
E: Error,
{
match v {
0 => Ok(false),
1 => Ok(true),
unexp => Err(Error::invalid_value(
Unexpected::Signed(unexp as i64),
&"0 or 1",
)),
}
}

fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: Error,
{
match v {
0 => Ok(false),
1 => Ok(true),
unexp => Err(Error::invalid_value(Unexpected::Unsigned(unexp), &"0 or 1")),
}
}

fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: Error,
{
match v {
0 => Ok(false),
1 => Ok(true),
unexp => Err(Error::invalid_value(Unexpected::Signed(unexp), &"0 or 1")),
}
}

fn visit_u128<E>(self, v: u128) -> Result<Self::Value, E>
where
E: Error,
{
match v {
0 => Ok(false),
1 => Ok(true),
unexp => Err(Error::invalid_value(
Unexpected::Unsigned(unexp as u64),
&"0 or 1",
)),
}
}

fn visit_i128<E>(self, v: i128) -> Result<Self::Value, E>
where
E: Error,
{
match v {
0 => Ok(false),
1 => Ok(true),
unexp => Err(Error::invalid_value(
Unexpected::Unsigned(unexp as u64),
&"0 or 1",
)),
}
}
}

deserializer.deserialize_u8(U8Visitor)
}
}

impl<'de> DeserializeAs<'de, bool> for BoolFromInt<Flexible> {
fn deserialize_as<D>(deserializer: D) -> Result<bool, D::Error>
where
D: Deserializer<'de>,
{
struct U8Visitor;
impl<'de> Visitor<'de> for U8Visitor {
type Value = bool;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("an integer")
}

fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
where
E: Error,
{
Ok(v != 0)
}

fn visit_i8<E>(self, v: i8) -> Result<Self::Value, E>
where
E: Error,
{
Ok(v != 0)
}

fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: Error,
{
Ok(v != 0)
}

fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: Error,
{
Ok(v != 0)
}

fn visit_u128<E>(self, v: u128) -> Result<Self::Value, E>
where
E: Error,
{
Ok(v != 0)
}

fn visit_i128<E>(self, v: i128) -> Result<Self::Value, E>
where
E: Error,
{
Ok(v != 0)
}
}

deserializer.deserialize_u8(U8Visitor)
}
}

// endregion
59 changes: 39 additions & 20 deletions serde_with/src/guide/serde_as_transformations.md
Expand Up @@ -4,26 +4,27 @@ This page lists the transformations implemented in this crate and supported by `

1. [Base64 encode bytes](#base64-encode-bytes)
2. [Big Array support](#big-array-support)
3. [Borrow from the input for `Cow` type](#borrow-from-the-input-for-cow-type)
4. [`Bytes` with more efficiency](#bytes-with-more-efficiency)
5. [Convert to an intermediate type using `Into`](#convert-to-an-intermediate-type-using-into)
6. [Convert to an intermediate type using `TryInto`](#convert-to-an-intermediate-type-using-tryinto)
7. [`Default` from `null`](#default-from-null)
8. [De/Serialize into `Vec`, ignoring errors](#deserialize-into-vec-ignoring-errors)
9. [De/Serialize with `FromStr` and `Display`](#deserialize-with-fromstr-and-display)
10. [`Duration` as seconds](#duration-as-seconds)
11. [Hex encode bytes](#hex-encode-bytes)
12. [Ignore deserialization errors](#ignore-deserialization-errors)
13. [`Maps` to `Vec` of enums](#maps-to-vec-of-enums)
14. [`Maps` to `Vec` of tuples](#maps-to-vec-of-tuples)
15. [`NaiveDateTime` like UTC timestamp](#naivedatetime-like-utc-timestamp)
16. [`None` as empty `String`](#none-as-empty-string)
17. [One or many elements into `Vec`](#one-or-many-elements-into-vec)
18. [Pick first successful deserialization](#pick-first-successful-deserialization)
19. [Timestamps as seconds since UNIX epoch](#timestamps-as-seconds-since-unix-epoch)
20. [Value into JSON String](#value-into-json-string)
21. [`Vec` of tuples to `Maps`](#vec-of-tuples-to-maps)
22. [Well-known time formats for `OffsetDateTime`](#well-known-time-formats-for-offsetdatetime)
3. [`bool` from integer](#bool-from-integer)
4. [Borrow from the input for `Cow` type](#borrow-from-the-input-for-cow-type)
5. [`Bytes` with more efficiency](#bytes-with-more-efficiency)
6. [Convert to an intermediate type using `Into`](#convert-to-an-intermediate-type-using-into)
7. [Convert to an intermediate type using `TryInto`](#convert-to-an-intermediate-type-using-tryinto)
8. [`Default` from `null`](#default-from-null)
9. [De/Serialize into `Vec`, ignoring errors](#deserialize-into-vec-ignoring-errors)
10. [De/Serialize with `FromStr` and `Display`](#deserialize-with-fromstr-and-display)
11. [`Duration` as seconds](#duration-as-seconds)
12. [Hex encode bytes](#hex-encode-bytes)
13. [Ignore deserialization errors](#ignore-deserialization-errors)
14. [`Maps` to `Vec` of enums](#maps-to-vec-of-enums)
15. [`Maps` to `Vec` of tuples](#maps-to-vec-of-tuples)
16. [`NaiveDateTime` like UTC timestamp](#naivedatetime-like-utc-timestamp)
17. [`None` as empty `String`](#none-as-empty-string)
18. [One or many elements into `Vec`](#one-or-many-elements-into-vec)
19. [Pick first successful deserialization](#pick-first-successful-deserialization)
20. [Timestamps as seconds since UNIX epoch](#timestamps-as-seconds-since-unix-epoch)
21. [Value into JSON String](#value-into-json-string)
22. [`Vec` of tuples to `Maps`](#vec-of-tuples-to-maps)
23. [Well-known time formats for `OffsetDateTime`](#well-known-time-formats-for-offsetdatetime)

## Base64 encode bytes

Expand Down Expand Up @@ -57,6 +58,22 @@ value: [[u8; 64]; 33],
"value": [[0,0,0,0,0,...], [0,0,0,...], ...],
```

## `bool` from integer

Deserialize an integer and convert it into a `bool`.
[`BoolFromInt<Strict>`] (default) deserializes 0 to `false` and `1` to `true`, other numbers are errors.
[`BoolFromInt<Flexible>`] deserializes any non-zero as `true`.
Serialization only emits 0/1.

```ignore
// Rust
#[serde_as(as = "BoolFromInt")] // BoolFromInt<Strict>
b: bool,

// JSON
"b": 1,
```

## Borrow from the input for `Cow` type

The types `Cow<'_, str>`, `Cow<'_, [u8]>`, or `Cow<'_, [u8; N]>` can borrow from the input, avoiding extra copies.
Expand Down Expand Up @@ -471,6 +488,8 @@ rfc_3339: OffsetDateTime,
These conversions are availble with the `time_0_3` feature flag.

[`Base64`]: crate::base64::Base64
[`BoolFromInt<Flexible>`]: crate::BoolFromInt
[`BoolFromInt<Strict>`]: crate::BoolFromInt
[`Bytes`]: crate::Bytes
[`chrono::DateTime<Local>`]: chrono_crate::DateTime
[`chrono::DateTime<Utc>`]: chrono_crate::DateTime
Expand Down
49 changes: 49 additions & 0 deletions serde_with/src/lib.rs
Expand Up @@ -1982,3 +1982,52 @@ pub struct BorrowCow;

#[derive(Copy, Clone, Debug, Default)]
pub struct VecSkipError<T>(PhantomData<T>);

/// Deserialize a boolean from a number
///
/// Deserialize a number (of `u8`) and turn it into a boolean.
/// The adapter supports a [`Strict`](crate::formats::Strict) and [`Flexible`](crate::formats::Flexible) format.
/// In `Strict` mode, the number must be `0` or `1`.
/// All other values produce an error.
/// In `Flexible` mode, the number any non-zero value is converted to `true`.
///
/// During serialization only `0` or `1` are ever emitted.
///
/// # Examples
///
/// ```rust
/// # #[cfg(feature = "macros")] {
/// # use serde::{Deserialize, Serialize};
/// # use serde_json::json;
/// # use serde_with::{serde_as, BoolFromInt};
/// #
/// #[serde_as]
/// # #[derive(Debug, PartialEq)]
/// #[derive(Deserialize, Serialize)]
/// struct Data(#[serde_as(as = "BoolFromInt")] bool);
///
/// let data = Data(true);
/// let j = json!(1);
/// // Ensure serialization and deserialization produce the expected results
/// assert_eq!(j, serde_json::to_value(&data).unwrap());
/// assert_eq!(data, serde_json::from_value(j).unwrap());
///
/// // false maps to 0
/// let data = Data(false);
/// let j = json!(0);
/// assert_eq!(j, serde_json::to_value(&data).unwrap());
/// assert_eq!(data, serde_json::from_value(j).unwrap());
//
/// #[serde_as]
/// # #[derive(Debug, PartialEq)]
/// #[derive(Deserialize, Serialize)]
/// struct Flexible(#[serde_as(as = "BoolFromInt<serde_with::formats::Flexible>")] bool);
///
/// // Flexible turns any non-zero number into true
/// let data = Flexible(true);
/// let j = json!(100);
/// assert_eq!(data, serde_json::from_value(j).unwrap());
/// # }
/// ```
#[derive(Copy, Clone, Debug, Default)]
pub struct BoolFromInt<S: formats::Strictness = formats::Strict>(PhantomData<S>);
9 changes: 9 additions & 0 deletions serde_with/src/ser/impls.rs
Expand Up @@ -727,4 +727,13 @@ impl<'a> SerializeAs<Cow<'a, [u8]>> for BorrowCow {
}
}

impl<STRICTNESS: Strictness> SerializeAs<bool> for BoolFromInt<STRICTNESS> {
fn serialize_as<S>(source: &bool, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u8(*source as u8)
}
}

// endregion