aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Vander Stoep <jeffv@google.com>2024-02-05 08:46:02 +0100
committerJeff Vander Stoep <jeffv@google.com>2024-02-05 08:46:02 +0100
commit85f2728d9e2c6040ac3a800ee9d30d6c331f3190 (patch)
tree04bb9065d05304dd095523bc84293fdeb99bb362
parent46d0d70012c6bdca02a001d825a69bfcfcc321a9 (diff)
downloadserde_json-85f2728d9e2c6040ac3a800ee9d30d6c331f3190.tar.gz
Upgrade serde_json to 1.0.113
This project was upgraded with external_updater. Usage: tools/external_updater/updater.sh update external/rust/crates/serde_json For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md Test: TreeHugger Change-Id: I4091b2292a767bd0120c88d4f585c7f2c55fd189
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--.github/workflows/ci.yml57
-rw-r--r--Android.bp4
-rw-r--r--Cargo.toml31
-rw-r--r--Cargo.toml.orig35
-rw-r--r--METADATA25
-rw-r--r--README.md2
-rw-r--r--build.rs39
-rw-r--r--src/de.rs287
-rw-r--r--src/error.rs126
-rw-r--r--src/io/core.rs2
-rw-r--r--src/lexical/algorithm.rs5
-rw-r--r--src/lexical/digit.rs5
-rw-r--r--src/lexical/errors.rs3
-rw-r--r--src/lexical/math.rs4
-rw-r--r--src/lexical/num.rs2
-rw-r--r--src/lib.rs22
-rw-r--r--src/macros.rs3
-rw-r--r--src/map.rs121
-rw-r--r--src/number.rs68
-rw-r--r--src/raw.rs268
-rw-r--r--src/read.rs2
-rw-r--r--src/ser.rs122
-rw-r--r--src/value/de.rs90
-rw-r--r--src/value/from.rs37
-rw-r--r--src/value/index.rs2
-rw-r--r--src/value/mod.rs30
-rw-r--r--src/value/partial_eq.rs14
-rw-r--r--src/value/ser.rs39
-rw-r--r--tests/lexical.rs5
-rw-r--r--tests/lexical/parse.rs2
-rw-r--r--tests/map.rs1
-rw-r--r--tests/regression/issue1004.rs12
-rw-r--r--tests/regression/issue845.rs1
-rw-r--r--tests/test.rs224
-rw-r--r--tests/ui/parse_key.stderr2
36 files changed, 1216 insertions, 478 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index d3c2d3e..fa385b5 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "a15bd0968639884ec7b73107360d58fd655e2071"
+ "sha1": "09d865b34b9701be52764dc9bf571b1a16e9d3dc"
},
"path_in_vcs": ""
} \ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index bfaffaf..d997982 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,6 +3,7 @@ name: CI
on:
push:
pull_request:
+ workflow_dispatch:
schedule: [cron: "40 1 * * *"]
permissions:
@@ -21,7 +22,7 @@ jobs:
os: [ubuntu, windows]
timeout-minutes: 45
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- run: cargo test
- run: cargo test --features preserve_order --tests -- --skip ui --exact
@@ -37,7 +38,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- rust: [beta, 1.56.1, 1.53.0, 1.46.0, 1.40.0, 1.38.0, 1.36.0]
+ rust: [beta, 1.64.0, 1.56.1]
os: [ubuntu]
include:
- rust: stable
@@ -47,27 +48,37 @@ jobs:
os: windows
timeout-minutes: 45
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{matrix.rust}}
targets: ${{matrix.target}}
- - run: cargo check
- - run: cargo check --features float_roundtrip
- - run: cargo check --features arbitrary_precision
- - run: cargo check --features raw_value
- - run: cargo check --features unbounded_depth
+ - run: cargo check --manifest-path tests/crate/Cargo.toml
+ - run: cargo check --manifest-path tests/crate/Cargo.toml --features float_roundtrip
+ - run: cargo check --manifest-path tests/crate/Cargo.toml --features arbitrary_precision
+ - run: cargo check --manifest-path tests/crate/Cargo.toml --features raw_value
+ - run: cargo check --manifest-path tests/crate/Cargo.toml --features unbounded_depth
- run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc
- run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,arbitrary_precision
- run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,raw_value
- - run: cargo check --features preserve_order
- if: matrix.rust != '1.53.0' && matrix.rust != '1.46.0' && matrix.rust != '1.45.0' && matrix.rust != '1.40.0' && matrix.rust != '1.38.0' && matrix.rust != '1.36.0'
- - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,preserve_order
- if: matrix.rust != '1.53.0' && matrix.rust != '1.46.0' && matrix.rust != '1.45.0' && matrix.rust != '1.40.0' && matrix.rust != '1.38.0' && matrix.rust != '1.36.0'
+ - run: cargo check --manifest-path tests/crate/Cargo.toml --features serde_json/preserve_order
+ if: matrix.rust != '1.56.1'
+ - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,serde_json/preserve_order
+ if: matrix.rust != '1.56.1'
- name: Build without std
run: cargo check --manifest-path tests/crate/Cargo.toml --target ${{matrix.target}} --no-default-features --features alloc
if: matrix.target
+ minimal:
+ name: Minimal versions
+ runs-on: ubuntu-latest
+ timeout-minutes: 45
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@nightly
+ - run: cargo generate-lockfile -Z minimal-versions
+ - run: cargo check --locked
+
miri:
name: Miri
runs-on: ubuntu-latest
@@ -75,8 +86,9 @@ jobs:
MIRIFLAGS: -Zmiri-strict-provenance
timeout-minutes: 45
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@miri
+ - run: cargo miri setup
- run: cargo miri test
- run: cargo miri test --features preserve_order,float_roundtrip,arbitrary_precision,raw_value
@@ -86,28 +98,29 @@ jobs:
if: github.event_name != 'pull_request'
timeout-minutes: 45
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@clippy
- run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic
- run: cargo clippy --all-features --tests -- -Dclippy::all -Dclippy::pedantic
- docs:
+ doc:
name: Documentation
runs-on: ubuntu-latest
timeout-minutes: 45
+ env:
+ RUSTDOCFLAGS: -Dwarnings
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- - run: cargo doc --features raw_value,unbounded_depth
- env:
- RUSTDOCFLAGS: --cfg docsrs
+ - uses: dtolnay/install@cargo-docs-rs
+ - run: cargo docs-rs
fuzz:
name: Fuzz
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- uses: dtolnay/install@cargo-fuzz
- run: cargo fuzz check
@@ -118,7 +131,7 @@ jobs:
if: github.event_name != 'pull_request'
timeout-minutes: 45
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: dtolnay/install@cargo-outdated
- - run: cargo outdated --workspace --exit-code 1
+ - run: cargo outdated --exit-code 1
- run: cargo outdated --manifest-path fuzz/Cargo.toml --exit-code 1
diff --git a/Android.bp b/Android.bp
index d448340..678bf00 100644
--- a/Android.bp
+++ b/Android.bp
@@ -43,9 +43,9 @@ rust_library {
host_supported: true,
crate_name: "serde_json",
cargo_env_compat: true,
- cargo_pkg_version: "1.0.94",
+ cargo_pkg_version: "1.0.113",
srcs: ["src/lib.rs"],
- edition: "2018",
+ edition: "2021",
features: [
"default",
"std",
diff --git a/Cargo.toml b/Cargo.toml
index fc2253d..447641d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,10 +10,10 @@
# See Cargo.toml.orig for the original contents.
[package]
-edition = "2018"
-rust-version = "1.36"
+edition = "2021"
+rust-version = "1.56"
name = "serde_json"
-version = "1.0.94"
+version = "1.0.113"
authors = [
"Erick Tryzelaar <erick.tryzelaar@gmail.com>",
"David Tolnay <dtolnay@gmail.com>",
@@ -36,12 +36,14 @@ repository = "https://github.com/serde-rs/json"
[package.metadata.docs.rs]
features = [
+ "preserve_order",
"raw_value",
"unbounded_depth",
]
rustdoc-args = [
"--cfg",
"docsrs",
+ "--generate-link-to-definition",
]
targets = ["x86_64-unknown-linux-gnu"]
@@ -52,8 +54,7 @@ features = ["raw_value"]
doc-scrape-examples = false
[dependencies.indexmap]
-version = "1.5.2"
-features = ["std"]
+version = "2.2.1"
optional = true
[dependencies.itoa]
@@ -63,36 +64,36 @@ version = "1.0"
version = "1.0"
[dependencies.serde]
-version = "1.0.100"
+version = "1.0.194"
default-features = false
[dev-dependencies.automod]
-version = "1.0"
+version = "1.0.11"
[dev-dependencies.indoc]
-version = "2.0"
+version = "2.0.2"
[dev-dependencies.ref-cast]
-version = "1.0"
+version = "1.0.18"
[dev-dependencies.rustversion]
-version = "1.0"
+version = "1.0.13"
[dev-dependencies.serde]
-version = "1.0.100"
+version = "1.0.194"
features = ["derive"]
[dev-dependencies.serde_bytes]
-version = "0.11"
+version = "0.11.10"
[dev-dependencies.serde_derive]
-version = "1.0"
+version = "1.0.166"
[dev-dependencies.serde_stacker]
-version = "0.1"
+version = "0.1.8"
[dev-dependencies.trybuild]
-version = "1.0.49"
+version = "1.0.81"
features = ["diff"]
[features]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index ee54284..cedde61 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,43 +1,40 @@
[package]
name = "serde_json"
-version = "1.0.94" # remember to update html_root_url
+version = "1.0.113"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
categories = ["encoding", "parser-implementations", "no-std"]
description = "A JSON serialization file format"
documentation = "https://docs.rs/serde_json"
-edition = "2018"
+edition = "2021"
keywords = ["json", "serde", "serialization"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/serde-rs/json"
-rust-version = "1.36"
+rust-version = "1.56"
[dependencies]
-serde = { version = "1.0.100", default-features = false }
-indexmap = { version = "1.5.2", features = ["std"], optional = true }
+indexmap = { version = "2.2.1", optional = true }
itoa = "1.0"
ryu = "1.0"
+serde = { version = "1.0.194", default-features = false }
[dev-dependencies]
-automod = "1.0"
-indoc = "2.0"
-ref-cast = "1.0"
-rustversion = "1.0"
-serde = { version = "1.0.100", features = ["derive"] }
-serde_bytes = "0.11"
-serde_derive = "1.0"
-serde_stacker = "0.1"
-trybuild = { version = "1.0.49", features = ["diff"] }
-
-[workspace]
-members = ["tests/crate"]
+automod = "1.0.11"
+indoc = "2.0.2"
+ref-cast = "1.0.18"
+rustversion = "1.0.13"
+serde = { version = "1.0.194", features = ["derive"] }
+serde_bytes = "0.11.10"
+serde_derive = "1.0.166"
+serde_stacker = "0.1.8"
+trybuild = { version = "1.0.81", features = ["diff"] }
[lib]
doc-scrape-examples = false
[package.metadata.docs.rs]
-features = ["raw_value", "unbounded_depth"]
+features = ["preserve_order", "raw_value", "unbounded_depth"]
targets = ["x86_64-unknown-linux-gnu"]
-rustdoc-args = ["--cfg", "docsrs"]
+rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"]
[package.metadata.playground]
features = ["raw_value"]
diff --git a/METADATA b/METADATA
index 9fece5b..a4bf6a6 100644
--- a/METADATA
+++ b/METADATA
@@ -1,23 +1,20 @@
# This project was upgraded with external_updater.
-# Usage: tools/external_updater/updater.sh update rust/crates/serde_json
-# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+# Usage: tools/external_updater/updater.sh update external/rust/crates/serde_json
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
name: "serde_json"
description: "A JSON serialization file format"
third_party {
- url {
- type: HOMEPAGE
- value: "https://crates.io/crates/serde_json"
- }
- url {
- type: ARCHIVE
- value: "https://static.crates.io/crates/serde_json/serde_json-1.0.94.crate"
- }
- version: "1.0.94"
license_type: NOTICE
last_upgrade_date {
- year: 2023
- month: 3
- day: 20
+ year: 2024
+ month: 2
+ day: 5
+ }
+ homepage: "https://crates.io/crates/serde_json"
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/serde_json/serde_json-1.0.113.crate"
+ version: "1.0.113"
}
}
diff --git a/README.md b/README.md
index d704979..a3ba288 100644
--- a/README.md
+++ b/README.md
@@ -76,7 +76,7 @@ enum Value {
A string of JSON data can be parsed into a `serde_json::Value` by the
[`serde_json::from_str`][from_str] function. There is also
-[`from_slice`][from_slice] for parsing from a byte slice &[u8] and
+[`from_slice`][from_slice] for parsing from a byte slice &\[u8\] and
[`from_reader`][from_reader] for parsing from any `io::Read` like a File or a
TCP stream.
diff --git a/build.rs b/build.rs
index 0e12602..dd09e62 100644
--- a/build.rs
+++ b/build.rs
@@ -1,6 +1,4 @@
use std::env;
-use std::process::Command;
-use std::str::{self, FromStr};
fn main() {
println!("cargo:rerun-if-changed=build.rs");
@@ -9,46 +7,11 @@ fn main() {
// src/lexical/math.rs for where this has an effect.
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
match target_arch.as_str() {
- "aarch64" | "mips64" | "powerpc64" | "x86_64" => {
+ "aarch64" | "mips64" | "powerpc64" | "x86_64" | "loongarch64" => {
println!("cargo:rustc-cfg=limb_width_64");
}
_ => {
println!("cargo:rustc-cfg=limb_width_32");
}
}
-
- let minor = match rustc_minor_version() {
- Some(minor) => minor,
- None => return,
- };
-
- // BTreeMap::get_key_value
- // https://blog.rust-lang.org/2019/12/19/Rust-1.40.0.html#additions-to-the-standard-library
- if minor < 40 {
- println!("cargo:rustc-cfg=no_btreemap_get_key_value");
- }
-
- // BTreeMap::remove_entry
- // https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#library-changes
- if minor < 45 {
- println!("cargo:rustc-cfg=no_btreemap_remove_entry");
- }
-
- // BTreeMap::retain
- // https://blog.rust-lang.org/2021/06/17/Rust-1.53.0.html#stabilized-apis
- if minor < 53 {
- println!("cargo:rustc-cfg=no_btreemap_retain");
- }
-}
-
-fn rustc_minor_version() -> Option<u32> {
- let rustc = env::var_os("RUSTC")?;
- let output = Command::new(rustc).arg("--version").output().ok()?;
- let version = str::from_utf8(&output.stdout).ok()?;
- let mut pieces = version.split('.');
- if pieces.next() != Some("rustc 1") {
- return None;
- }
- let next = pieces.next()?;
- u32::from_str(next).ok()
}
diff --git a/src/de.rs b/src/de.rs
index 88d0f26..7154f84 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -22,6 +22,7 @@ use crate::number::NumberDeserializer;
pub use crate::read::{Read, SliceRead, StrRead};
#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub use crate::read::IoRead;
//////////////////////////////////////////////////////////////////////////////
@@ -209,7 +210,7 @@ impl<'de, R: Read<'de>> Deserializer<R> {
self.disable_recursion_limit = true;
}
- fn peek(&mut self) -> Result<Option<u8>> {
+ pub(crate) fn peek(&mut self) -> Result<Option<u8>> {
self.read.peek()
}
@@ -248,7 +249,7 @@ impl<'de, R: Read<'de>> Deserializer<R> {
fn parse_whitespace(&mut self) -> Result<Option<u8>> {
loop {
match tri!(self.peek()) {
- Some(b' ') | Some(b'\n') | Some(b'\t') | Some(b'\r') => {
+ Some(b' ' | b'\n' | b'\t' | b'\r') => {
self.eat_char();
}
other => {
@@ -309,9 +310,9 @@ impl<'de, R: Read<'de>> Deserializer<R> {
self.fix_position(err)
}
- fn deserialize_number<V>(&mut self, visitor: V) -> Result<V::Value>
+ pub(crate) fn deserialize_number<'any, V>(&mut self, visitor: V) -> Result<V::Value>
where
- V: de::Visitor<'de>,
+ V: de::Visitor<'any>,
{
let peek = match tri!(self.parse_whitespace()) {
Some(b) => b,
@@ -335,6 +336,79 @@ impl<'de, R: Read<'de>> Deserializer<R> {
}
}
+ #[cfg(feature = "float_roundtrip")]
+ pub(crate) fn do_deserialize_f32<'any, V>(&mut self, visitor: V) -> Result<V::Value>
+ where
+ V: de::Visitor<'any>,
+ {
+ self.single_precision = true;
+ let val = self.deserialize_number(visitor);
+ self.single_precision = false;
+ val
+ }
+
+ pub(crate) fn do_deserialize_i128<'any, V>(&mut self, visitor: V) -> Result<V::Value>
+ where
+ V: de::Visitor<'any>,
+ {
+ let mut buf = String::new();
+
+ match tri!(self.parse_whitespace()) {
+ Some(b'-') => {
+ self.eat_char();
+ buf.push('-');
+ }
+ Some(_) => {}
+ None => {
+ return Err(self.peek_error(ErrorCode::EofWhileParsingValue));
+ }
+ };
+
+ tri!(self.scan_integer128(&mut buf));
+
+ let value = match buf.parse() {
+ Ok(int) => visitor.visit_i128(int),
+ Err(_) => {
+ return Err(self.error(ErrorCode::NumberOutOfRange));
+ }
+ };
+
+ match value {
+ Ok(value) => Ok(value),
+ Err(err) => Err(self.fix_position(err)),
+ }
+ }
+
+ pub(crate) fn do_deserialize_u128<'any, V>(&mut self, visitor: V) -> Result<V::Value>
+ where
+ V: de::Visitor<'any>,
+ {
+ match tri!(self.parse_whitespace()) {
+ Some(b'-') => {
+ return Err(self.peek_error(ErrorCode::NumberOutOfRange));
+ }
+ Some(_) => {}
+ None => {
+ return Err(self.peek_error(ErrorCode::EofWhileParsingValue));
+ }
+ }
+
+ let mut buf = String::new();
+ tri!(self.scan_integer128(&mut buf));
+
+ let value = match buf.parse() {
+ Ok(int) => visitor.visit_u128(int),
+ Err(_) => {
+ return Err(self.error(ErrorCode::NumberOutOfRange));
+ }
+ };
+
+ match value {
+ Ok(value) => Ok(value),
+ Err(err) => Err(self.fix_position(err)),
+ }
+ }
+
fn scan_integer128(&mut self, buf: &mut String) -> Result<()> {
match tri!(self.next_char_or_null()) {
b'0' => {
@@ -860,7 +934,7 @@ impl<'de, R: Read<'de>> Deserializer<R> {
if !positive {
buf.push('-');
}
- self.scan_integer(&mut buf)?;
+ tri!(self.scan_integer(&mut buf));
if positive {
if let Ok(unsigned) = buf.parse() {
return Ok(ParserNumber::U64(unsigned));
@@ -913,7 +987,7 @@ impl<'de, R: Read<'de>> Deserializer<R> {
fn scan_number(&mut self, buf: &mut String) -> Result<()> {
match tri!(self.peek_or_null()) {
b'.' => self.scan_decimal(buf),
- e @ b'e' | e @ b'E' => self.scan_exponent(e as char, buf),
+ e @ (b'e' | b'E') => self.scan_exponent(e as char, buf),
_ => Ok(()),
}
}
@@ -938,7 +1012,7 @@ impl<'de, R: Read<'de>> Deserializer<R> {
}
match tri!(self.peek_or_null()) {
- e @ b'e' | e @ b'E' => self.scan_exponent(e as char, buf),
+ e @ (b'e' | b'E') => self.scan_exponent(e as char, buf),
_ => Ok(()),
}
}
@@ -1059,7 +1133,7 @@ impl<'de, R: Read<'de>> Deserializer<R> {
tri!(self.read.ignore_str());
None
}
- frame @ b'[' | frame @ b'{' => {
+ frame @ (b'[' | b'{') => {
self.scratch.extend(enclosing.take());
self.eat_char();
Some(frame)
@@ -1204,9 +1278,9 @@ impl<'de, R: Read<'de>> Deserializer<R> {
where
V: de::Visitor<'de>,
{
- self.parse_whitespace()?;
+ tri!(self.parse_whitespace());
self.read.begin_raw_buffering();
- self.ignore_value()?;
+ tri!(self.ignore_value());
self.read.end_raw_buffering(visitor)
}
}
@@ -1258,11 +1332,15 @@ static POW10: [f64; 309] = [
macro_rules! deserialize_number {
($method:ident) => {
+ deserialize_number!($method, deserialize_number);
+ };
+
+ ($method:ident, $using:ident) => {
fn $method<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
- self.deserialize_number(visitor)
+ self.$using(visitor)
}
};
}
@@ -1424,77 +1502,9 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer<R> {
deserialize_number!(deserialize_f64);
#[cfg(feature = "float_roundtrip")]
- fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
- where
- V: de::Visitor<'de>,
- {
- self.single_precision = true;
- let val = self.deserialize_number(visitor);
- self.single_precision = false;
- val
- }
-
- fn deserialize_i128<V>(self, visitor: V) -> Result<V::Value>
- where
- V: de::Visitor<'de>,
- {
- let mut buf = String::new();
-
- match tri!(self.parse_whitespace()) {
- Some(b'-') => {
- self.eat_char();
- buf.push('-');
- }
- Some(_) => {}
- None => {
- return Err(self.peek_error(ErrorCode::EofWhileParsingValue));
- }
- };
-
- tri!(self.scan_integer128(&mut buf));
-
- let value = match buf.parse() {
- Ok(int) => visitor.visit_i128(int),
- Err(_) => {
- return Err(self.error(ErrorCode::NumberOutOfRange));
- }
- };
-
- match value {
- Ok(value) => Ok(value),
- Err(err) => Err(self.fix_position(err)),
- }
- }
-
- fn deserialize_u128<V>(self, visitor: V) -> Result<V::Value>
- where
- V: de::Visitor<'de>,
- {
- match tri!(self.parse_whitespace()) {
- Some(b'-') => {
- return Err(self.peek_error(ErrorCode::NumberOutOfRange));
- }
- Some(_) => {}
- None => {
- return Err(self.peek_error(ErrorCode::EofWhileParsingValue));
- }
- }
-
- let mut buf = String::new();
- tri!(self.scan_integer128(&mut buf));
-
- let value = match buf.parse() {
- Ok(int) => visitor.visit_u128(int),
- Err(_) => {
- return Err(self.error(ErrorCode::NumberOutOfRange));
- }
- };
-
- match value {
- Ok(value) => Ok(value),
- Err(err) => Err(self.fix_position(err)),
- }
- }
+ deserialize_number!(deserialize_f32, do_deserialize_f32);
+ deserialize_number!(deserialize_i128, do_deserialize_i128);
+ deserialize_number!(deserialize_u128, do_deserialize_u128);
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
where
@@ -2118,24 +2128,47 @@ struct MapKey<'a, R: 'a> {
de: &'a mut Deserializer<R>,
}
-macro_rules! deserialize_integer_key {
- ($method:ident => $visit:ident) => {
+macro_rules! deserialize_numeric_key {
+ ($method:ident) => {
+ fn $method<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: de::Visitor<'de>,
+ {
+ self.deserialize_number(visitor)
+ }
+ };
+
+ ($method:ident, $delegate:ident) => {
fn $method<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.de.eat_char();
- self.de.scratch.clear();
- let string = tri!(self.de.read.parse_str(&mut self.de.scratch));
- match (string.parse(), string) {
- (Ok(integer), _) => visitor.$visit(integer),
- (Err(_), Reference::Borrowed(s)) => visitor.visit_borrowed_str(s),
- (Err(_), Reference::Copied(s)) => visitor.visit_str(s),
+
+ match tri!(self.de.peek()) {
+ Some(b'0'..=b'9' | b'-') => {}
+ _ => return Err(self.de.error(ErrorCode::ExpectedNumericKey)),
+ }
+
+ let value = tri!(self.de.$delegate(visitor));
+
+ match tri!(self.de.peek()) {
+ Some(b'"') => self.de.eat_char(),
+ _ => return Err(self.de.peek_error(ErrorCode::ExpectedDoubleQuote)),
}
+
+ Ok(value)
}
};
}
+impl<'de, 'a, R> MapKey<'a, R>
+where
+ R: Read<'de>,
+{
+ deserialize_numeric_key!(deserialize_number, deserialize_number);
+}
+
impl<'de, 'a, R> de::Deserializer<'de> for MapKey<'a, R>
where
R: Read<'de>,
@@ -2155,16 +2188,56 @@ where
}
}
- deserialize_integer_key!(deserialize_i8 => visit_i8);
- deserialize_integer_key!(deserialize_i16 => visit_i16);
- deserialize_integer_key!(deserialize_i32 => visit_i32);
- deserialize_integer_key!(deserialize_i64 => visit_i64);
- deserialize_integer_key!(deserialize_i128 => visit_i128);
- deserialize_integer_key!(deserialize_u8 => visit_u8);
- deserialize_integer_key!(deserialize_u16 => visit_u16);
- deserialize_integer_key!(deserialize_u32 => visit_u32);
- deserialize_integer_key!(deserialize_u64 => visit_u64);
- deserialize_integer_key!(deserialize_u128 => visit_u128);
+ deserialize_numeric_key!(deserialize_i8);
+ deserialize_numeric_key!(deserialize_i16);
+ deserialize_numeric_key!(deserialize_i32);
+ deserialize_numeric_key!(deserialize_i64);
+ deserialize_numeric_key!(deserialize_i128, deserialize_i128);
+ deserialize_numeric_key!(deserialize_u8);
+ deserialize_numeric_key!(deserialize_u16);
+ deserialize_numeric_key!(deserialize_u32);
+ deserialize_numeric_key!(deserialize_u64);
+ deserialize_numeric_key!(deserialize_u128, deserialize_u128);
+ #[cfg(not(feature = "float_roundtrip"))]
+ deserialize_numeric_key!(deserialize_f32);
+ #[cfg(feature = "float_roundtrip")]
+ deserialize_numeric_key!(deserialize_f32, deserialize_f32);
+ deserialize_numeric_key!(deserialize_f64);
+
+ fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
+ where
+ V: de::Visitor<'de>,
+ {
+ self.de.eat_char();
+
+ let peek = match tri!(self.de.next_char()) {
+ Some(b) => b,
+ None => {
+ return Err(self.de.peek_error(ErrorCode::EofWhileParsingValue));
+ }
+ };
+
+ let value = match peek {
+ b't' => {
+ tri!(self.de.parse_ident(b"rue\""));
+ visitor.visit_bool(true)
+ }
+ b'f' => {
+ tri!(self.de.parse_ident(b"alse\""));
+ visitor.visit_bool(false)
+ }
+ _ => {
+ self.de.scratch.clear();
+ let s = tri!(self.de.read.parse_str(&mut self.de.scratch));
+ Err(de::Error::invalid_type(Unexpected::Str(&s), &visitor))
+ }
+ };
+
+ match value {
+ Ok(value) => Ok(value),
+ Err(err) => Err(self.de.fix_position(err)),
+ }
+ }
#[inline]
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
@@ -2221,8 +2294,8 @@ where
}
forward_to_deserialize_any! {
- bool f32 f64 char str string unit unit_struct seq tuple tuple_struct map
- struct identifier ignored_any
+ char str string unit unit_struct seq tuple tuple_struct map struct
+ identifier ignored_any
}
}
@@ -2318,8 +2391,8 @@ where
fn peek_end_of_value(&mut self) -> Result<()> {
match tri!(self.de.peek()) {
- Some(b' ') | Some(b'\n') | Some(b'\t') | Some(b'\r') | Some(b'"') | Some(b'[')
- | Some(b']') | Some(b'{') | Some(b'}') | Some(b',') | Some(b':') | None => Ok(()),
+ Some(b' ' | b'\n' | b'\t' | b'\r' | b'"' | b'[' | b']' | b'{' | b'}' | b',' | b':')
+ | None => Ok(()),
Some(_) => {
let position = self.de.read.peek_position();
Err(Error::syntax(
@@ -2369,7 +2442,7 @@ where
if self_delineated_value {
Ok(value)
} else {
- self.peek_end_of_value().map(|_| value)
+ self.peek_end_of_value().map(|()| value)
}
}
Err(e) => {
@@ -2408,9 +2481,9 @@ where
Ok(value)
}
-/// Deserialize an instance of type `T` from an IO stream of JSON.
+/// Deserialize an instance of type `T` from an I/O stream of JSON.
///
-/// The content of the IO stream is deserialized directly from the stream
+/// The content of the I/O stream is deserialized directly from the stream
/// without being buffered in memory by serde_json.
///
/// When reading from a source against which short reads are not efficient, such
diff --git a/src/error.rs b/src/error.rs
index 0898baf..fbf9eb1 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -9,6 +9,8 @@ use core::str::FromStr;
use serde::{de, ser};
#[cfg(feature = "std")]
use std::error;
+#[cfg(feature = "std")]
+use std::io::ErrorKind;
/// This type represents all possible errors that can occur when serializing or
/// deserializing JSON data.
@@ -36,15 +38,16 @@ impl Error {
/// The first character in the input and any characters immediately
/// following a newline character are in column 1.
///
- /// Note that errors may occur in column 0, for example if a read from an IO
- /// stream fails immediately following a previously read newline character.
+ /// Note that errors may occur in column 0, for example if a read from an
+ /// I/O stream fails immediately following a previously read newline
+ /// character.
pub fn column(&self) -> usize {
self.err.column
}
/// Categorizes the cause of this error.
///
- /// - `Category::Io` - failure to read or write bytes on an IO stream
+ /// - `Category::Io` - failure to read or write bytes on an I/O stream
/// - `Category::Syntax` - input that is not syntactically valid JSON
/// - `Category::Data` - input data that is semantically incorrect
/// - `Category::Eof` - unexpected end of the input data
@@ -61,12 +64,15 @@ impl Error {
| ErrorCode::ExpectedObjectCommaOrEnd
| ErrorCode::ExpectedSomeIdent
| ErrorCode::ExpectedSomeValue
+ | ErrorCode::ExpectedDoubleQuote
| ErrorCode::InvalidEscape
| ErrorCode::InvalidNumber
| ErrorCode::NumberOutOfRange
| ErrorCode::InvalidUnicodeCodePoint
| ErrorCode::ControlCharacterWhileParsingString
| ErrorCode::KeyMustBeAString
+ | ErrorCode::ExpectedNumericKey
+ | ErrorCode::FloatKeyMustBeFinite
| ErrorCode::LoneLeadingSurrogateInHexEscape
| ErrorCode::TrailingComma
| ErrorCode::TrailingCharacters
@@ -76,7 +82,7 @@ impl Error {
}
/// Returns true if this error was caused by a failure to read or write
- /// bytes on an IO stream.
+ /// bytes on an I/O stream.
pub fn is_io(&self) -> bool {
self.classify() == Category::Io
}
@@ -104,12 +110,61 @@ impl Error {
pub fn is_eof(&self) -> bool {
self.classify() == Category::Eof
}
+
+ /// The kind reported by the underlying standard library I/O error, if this
+ /// error was caused by a failure to read or write bytes on an I/O stream.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use serde_json::Value;
+ /// use std::io::{self, ErrorKind, Read};
+ /// use std::process;
+ ///
+ /// struct ReaderThatWillTimeOut<'a>(&'a [u8]);
+ ///
+ /// impl<'a> Read for ReaderThatWillTimeOut<'a> {
+ /// fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ /// if self.0.is_empty() {
+ /// Err(io::Error::new(ErrorKind::TimedOut, "timed out"))
+ /// } else {
+ /// self.0.read(buf)
+ /// }
+ /// }
+ /// }
+ ///
+ /// fn main() {
+ /// let reader = ReaderThatWillTimeOut(br#" {"k": "#);
+ ///
+ /// let _: Value = match serde_json::from_reader(reader) {
+ /// Ok(value) => value,
+ /// Err(error) => {
+ /// if error.io_error_kind() == Some(ErrorKind::TimedOut) {
+ /// // Maybe this application needs to retry certain kinds of errors.
+ ///
+ /// # return;
+ /// } else {
+ /// eprintln!("error: {}", error);
+ /// process::exit(1);
+ /// }
+ /// }
+ /// };
+ /// }
+ /// ```
+ #[cfg(feature = "std")]
+ pub fn io_error_kind(&self) -> Option<ErrorKind> {
+ if let ErrorCode::Io(io_error) = &self.err.code {
+ Some(io_error.kind())
+ } else {
+ None
+ }
+ }
}
/// Categorizes the cause of a `serde_json::Error`.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Category {
- /// The error was caused by a failure to read or write bytes on an IO
+ /// The error was caused by a failure to read or write bytes on an I/O
/// stream.
Io,
@@ -134,8 +189,8 @@ pub enum Category {
impl From<Error> for io::Error {
/// Convert a `serde_json::Error` into an `io::Error`.
///
- /// JSON syntax and data errors are turned into `InvalidData` IO errors.
- /// EOF errors are turned into `UnexpectedEof` IO errors.
+ /// JSON syntax and data errors are turned into `InvalidData` I/O errors.
+ /// EOF errors are turned into `UnexpectedEof` I/O errors.
///
/// ```
/// use std::io;
@@ -165,8 +220,8 @@ impl From<Error> for io::Error {
} else {
match j.classify() {
Category::Io => unreachable!(),
- Category::Syntax | Category::Data => io::Error::new(io::ErrorKind::InvalidData, j),
- Category::Eof => io::Error::new(io::ErrorKind::UnexpectedEof, j),
+ Category::Syntax | Category::Data => io::Error::new(ErrorKind::InvalidData, j),
+ Category::Eof => io::Error::new(ErrorKind::UnexpectedEof, j),
}
}
}
@@ -182,7 +237,7 @@ pub(crate) enum ErrorCode {
/// Catchall for syntax error messages
Message(Box<str>),
- /// Some IO error occurred while serializing or deserializing.
+ /// Some I/O error occurred while serializing or deserializing.
Io(io::Error),
/// EOF while parsing a list.
@@ -212,6 +267,9 @@ pub(crate) enum ErrorCode {
/// Expected this character to start a JSON value.
ExpectedSomeValue,
+ /// Expected this character to be a `"`.
+ ExpectedDoubleQuote,
+
/// Invalid hex escape code.
InvalidEscape,
@@ -230,6 +288,12 @@ pub(crate) enum ErrorCode {
/// Object key is not a string.
KeyMustBeAString,
+ /// Contents of key were supposed to be a number.
+ ExpectedNumericKey,
+
+ /// Object key is a non-finite float value.
+ FloatKeyMustBeFinite,
+
/// Lone leading surrogate in hex escape.
LoneLeadingSurrogateInHexEscape,
@@ -296,6 +360,7 @@ impl Display for ErrorCode {
ErrorCode::ExpectedObjectCommaOrEnd => f.write_str("expected `,` or `}`"),
ErrorCode::ExpectedSomeIdent => f.write_str("expected ident"),
ErrorCode::ExpectedSomeValue => f.write_str("expected value"),
+ ErrorCode::ExpectedDoubleQuote => f.write_str("expected `\"`"),
ErrorCode::InvalidEscape => f.write_str("invalid escape"),
ErrorCode::InvalidNumber => f.write_str("invalid number"),
ErrorCode::NumberOutOfRange => f.write_str("number out of range"),
@@ -304,6 +369,12 @@ impl Display for ErrorCode {
f.write_str("control character (\\u0000-\\u001F) found while parsing a string")
}
ErrorCode::KeyMustBeAString => f.write_str("key must be a string"),
+ ErrorCode::ExpectedNumericKey => {
+ f.write_str("invalid value: expected key to be a number in quotes")
+ }
+ ErrorCode::FloatKeyMustBeFinite => {
+ f.write_str("float key must be finite (got NaN or +/-inf)")
+ }
ErrorCode::LoneLeadingSurrogateInHexEscape => {
f.write_str("lone leading surrogate in hex escape")
}
@@ -367,11 +438,20 @@ impl de::Error for Error {
#[cold]
fn invalid_type(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self {
- if let de::Unexpected::Unit = unexp {
- Error::custom(format_args!("invalid type: null, expected {}", exp))
- } else {
- Error::custom(format_args!("invalid type: {}, expected {}", unexp, exp))
- }
+ Error::custom(format_args!(
+ "invalid type: {}, expected {}",
+ JsonUnexpected(unexp),
+ exp,
+ ))
+ }
+
+ #[cold]
+ fn invalid_value(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self {
+ Error::custom(format_args!(
+ "invalid value: {}, expected {}",
+ JsonUnexpected(unexp),
+ exp,
+ ))
}
}
@@ -382,6 +462,22 @@ impl ser::Error for Error {
}
}
+struct JsonUnexpected<'a>(de::Unexpected<'a>);
+
+impl<'a> Display for JsonUnexpected<'a> {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ match self.0 {
+ de::Unexpected::Unit => formatter.write_str("null"),
+ de::Unexpected::Float(value) => write!(
+ formatter,
+ "floating point `{}`",
+ ryu::Buffer::new().format(value),
+ ),
+ unexp => Display::fmt(&unexp, formatter),
+ }
+ }
+}
+
// Parse our own error message that looks like "{} at line {} column {}" to work
// around erased-serde round-tripping the error through de::Error::custom.
fn make_error(mut msg: String) -> Error {
diff --git a/src/io/core.rs b/src/io/core.rs
index 465ab8b..54c8ddf 100644
--- a/src/io/core.rs
+++ b/src/io/core.rs
@@ -9,7 +9,7 @@ pub enum ErrorKind {
Other,
}
-// IO errors can never occur in no-std mode. All our no-std IO implementations
+// I/O errors can never occur in no-std mode. All our no-std I/O implementations
// are infallible.
pub struct Error;
diff --git a/src/lexical/algorithm.rs b/src/lexical/algorithm.rs
index a2cbf18..eaa5e7e 100644
--- a/src/lexical/algorithm.rs
+++ b/src/lexical/algorithm.rs
@@ -51,7 +51,10 @@ where
// Compute the product of the power, if it overflows,
// prematurely return early, otherwise, if we didn't overshoot,
// we can get an exact value.
- let value = mantissa.checked_mul(power)?;
+ let value = match mantissa.checked_mul(power) {
+ None => return None,
+ Some(value) => value,
+ };
if value >> mantissa_size != 0 {
None
} else {
diff --git a/src/lexical/digit.rs b/src/lexical/digit.rs
index 882aa9e..3d150a1 100644
--- a/src/lexical/digit.rs
+++ b/src/lexical/digit.rs
@@ -11,5 +11,8 @@ pub(crate) fn to_digit(c: u8) -> Option<u32> {
// Add digit to mantissa.
#[inline]
pub(crate) fn add_digit(value: u64, digit: u32) -> Option<u64> {
- value.checked_mul(10)?.checked_add(digit as u64)
+ match value.checked_mul(10) {
+ None => None,
+ Some(n) => n.checked_add(digit as u64),
+ }
}
diff --git a/src/lexical/errors.rs b/src/lexical/errors.rs
index cad4bd3..f4f41cd 100644
--- a/src/lexical/errors.rs
+++ b/src/lexical/errors.rs
@@ -5,8 +5,7 @@
//! This estimates the error in a floating-point representation.
//!
//! This implementation is loosely based off the Golang implementation,
-//! found here:
-//! https://golang.org/src/strconv/atof.go
+//! found here: <https://golang.org/src/strconv/atof.go>
use super::float::*;
use super::num::*;
diff --git a/src/lexical/math.rs b/src/lexical/math.rs
index 37cc1d2..d7122bf 100644
--- a/src/lexical/math.rs
+++ b/src/lexical/math.rs
@@ -336,7 +336,7 @@ mod small {
pub fn imul(x: &mut Vec<Limb>, y: Limb) {
// Multiply iteratively over all elements, adding the carry each time.
let mut carry: Limb = 0;
- for xi in x.iter_mut() {
+ for xi in &mut *x {
carry = scalar::imul(xi, y, carry);
}
@@ -482,7 +482,7 @@ mod small {
let rshift = bits - n;
let lshift = n;
let mut prev: Limb = 0;
- for xi in x.iter_mut() {
+ for xi in &mut *x {
let tmp = *xi;
*xi <<= lshift;
*xi |= prev >> rshift;
diff --git a/src/lexical/num.rs b/src/lexical/num.rs
index e47e003..af10afd 100644
--- a/src/lexical/num.rs
+++ b/src/lexical/num.rs
@@ -223,7 +223,7 @@ pub trait Float: Number {
const NEGATIVE_INFINITY_BITS: Self::Unsigned;
/// Size of the significand (mantissa) without hidden bit.
const MANTISSA_SIZE: i32;
- /// Bias of the exponet
+ /// Bias of the exponent
const EXPONENT_BIAS: i32;
/// Exponent portion of a denormal float.
const DENORMAL_EXPONENT: i32;
diff --git a/src/lib.rs b/src/lib.rs
index bd49763..aa3fcfc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -55,10 +55,9 @@
//! ```
//!
//! A string of JSON data can be parsed into a `serde_json::Value` by the
-//! [`serde_json::from_str`][from_str] function. There is also
-//! [`from_slice`][from_slice] for parsing from a byte slice &[u8] and
-//! [`from_reader`][from_reader] for parsing from any `io::Read` like a File or
-//! a TCP stream.
+//! [`serde_json::from_str`][from_str] function. There is also [`from_slice`]
+//! for parsing from a byte slice &\[u8\] and [`from_reader`] for parsing from
+//! any `io::Read` like a File or a TCP stream.
//!
//! ```
//! use serde_json::{Result, Value};
@@ -300,7 +299,7 @@
//! [macro]: crate::json
//! [`serde-json-core`]: https://github.com/rust-embedded-community/serde-json-core
-#![doc(html_root_url = "https://docs.rs/serde_json/1.0.94")]
+#![doc(html_root_url = "https://docs.rs/serde_json/1.0.113")]
// Ignored clippy lints
#![allow(
clippy::collapsible_else_if,
@@ -315,18 +314,13 @@
clippy::match_single_binding,
clippy::needless_doctest_main,
clippy::needless_late_init,
- // clippy bug: https://github.com/rust-lang/rust-clippy/issues/8366
- clippy::ptr_arg,
clippy::return_self_not_must_use,
clippy::transmute_ptr_to_ptr,
- clippy::unnecessary_wraps,
- // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704
- clippy::unnested_or_patterns,
+ clippy::unconditional_recursion, // https://github.com/rust-lang/rust-clippy/issues/12133
+ clippy::unnecessary_wraps
)]
// Ignored clippy_pedantic lints
#![allow(
- // buggy
- clippy::iter_not_returning_iterator, // https://github.com/rust-lang/rust-clippy/issues/8285
// Deserializer::from_str, into_iter
clippy::should_implement_trait,
// integer and float ser/de requires these sorts of casts
@@ -362,6 +356,8 @@
clippy::missing_errors_doc,
clippy::must_use_candidate,
)]
+// Restrictions
+#![deny(clippy::question_mark_used)]
#![allow(non_upper_case_globals)]
#![deny(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
@@ -370,6 +366,7 @@
extern crate alloc;
#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[doc(inline)]
pub use crate::de::from_reader;
#[doc(inline)]
@@ -379,6 +376,7 @@ pub use crate::error::{Error, Result};
#[doc(inline)]
pub use crate::ser::{to_string, to_string_pretty, to_vec, to_vec_pretty};
#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[doc(inline)]
pub use crate::ser::{to_writer, to_writer_pretty, Serializer};
#[doc(inline)]
diff --git a/src/macros.rs b/src/macros.rs
index 5287998..e8c6cd2 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -10,7 +10,8 @@
/// "features": [
/// "serde",
/// "json"
-/// ]
+/// ],
+/// "homepage": null
/// }
/// });
/// ```
diff --git a/src/map.rs b/src/map.rs
index 3e8a381..1d74e1b 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -11,7 +11,7 @@ use alloc::string::String;
use core::borrow::Borrow;
use core::fmt::{self, Debug};
use core::hash::Hash;
-use core::iter::{FromIterator, FusedIterator};
+use core::iter::FusedIterator;
#[cfg(feature = "preserve_order")]
use core::mem;
use core::ops;
@@ -106,7 +106,6 @@ impl Map<String, Value> {
/// The key may be any borrowed form of the map's key type, but the ordering
/// on the borrowed form *must* match the ordering on the key type.
#[inline]
- #[cfg(any(feature = "preserve_order", not(no_btreemap_get_key_value)))]
pub fn get_key_value<Q>(&self, key: &Q) -> Option<(&String, &Value)>
where
String: Borrow<Q>,
@@ -131,6 +130,12 @@ impl Map<String, Value> {
///
/// The key may be any borrowed form of the map's key type, but the ordering
/// on the borrowed form *must* match the ordering on the key type.
+ ///
+ /// If serde_json's "preserve_order" is enabled, `.remove(key)` is
+ /// equivalent to [`.swap_remove(key)`][Self::swap_remove], replacing this
+ /// entry's position with the last element. If you need to preserve the
+ /// relative order of the keys in the map, use
+ /// [`.shift_remove(key)`][Self::shift_remove] instead.
#[inline]
pub fn remove<Q>(&mut self, key: &Q) -> Option<Value>
where
@@ -138,7 +143,7 @@ impl Map<String, Value> {
Q: ?Sized + Ord + Eq + Hash,
{
#[cfg(feature = "preserve_order")]
- return self.map.swap_remove(key);
+ return self.swap_remove(key);
#[cfg(not(feature = "preserve_order"))]
return self.map.remove(key);
}
@@ -148,49 +153,86 @@ impl Map<String, Value> {
///
/// The key may be any borrowed form of the map's key type, but the ordering
/// on the borrowed form *must* match the ordering on the key type.
+ ///
+ /// If serde_json's "preserve_order" is enabled, `.remove_entry(key)` is
+ /// equivalent to [`.swap_remove_entry(key)`][Self::swap_remove_entry],
+ /// replacing this entry's position with the last element. If you need to
+ /// preserve the relative order of the keys in the map, use
+ /// [`.shift_remove_entry(key)`][Self::shift_remove_entry] instead.
+ #[inline]
pub fn remove_entry<Q>(&mut self, key: &Q) -> Option<(String, Value)>
where
String: Borrow<Q>,
Q: ?Sized + Ord + Eq + Hash,
{
- #[cfg(any(feature = "preserve_order", not(no_btreemap_remove_entry)))]
+ #[cfg(feature = "preserve_order")]
+ return self.swap_remove_entry(key);
+ #[cfg(not(feature = "preserve_order"))]
return self.map.remove_entry(key);
- #[cfg(all(
- not(feature = "preserve_order"),
- no_btreemap_remove_entry,
- not(no_btreemap_get_key_value),
- ))]
- {
- let (key, _value) = self.map.get_key_value(key)?;
- let key = key.clone();
- let value = self.map.remove::<String>(&key)?;
- Some((key, value))
- }
- #[cfg(all(
- not(feature = "preserve_order"),
- no_btreemap_remove_entry,
- no_btreemap_get_key_value,
- ))]
- {
- use core::ops::{Bound, RangeBounds};
-
- struct Key<'a, Q: ?Sized>(&'a Q);
-
- impl<'a, Q: ?Sized> RangeBounds<Q> for Key<'a, Q> {
- fn start_bound(&self) -> Bound<&Q> {
- Bound::Included(self.0)
- }
- fn end_bound(&self) -> Bound<&Q> {
- Bound::Included(self.0)
- }
- }
+ }
- let mut range = self.map.range(Key(key));
- let (key, _value) = range.next()?;
- let key = key.clone();
- let value = self.map.remove::<String>(&key)?;
- Some((key, value))
- }
+ /// Removes and returns the value corresponding to the key from the map.
+ ///
+ /// Like [`Vec::swap_remove`], the entry is removed by swapping it with the
+ /// last element of the map and popping it off. This perturbs the position
+ /// of what used to be the last element!
+ #[cfg(feature = "preserve_order")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))]
+ #[inline]
+ pub fn swap_remove<Q>(&mut self, key: &Q) -> Option<Value>
+ where
+ String: Borrow<Q>,
+ Q: ?Sized + Ord + Eq + Hash,
+ {
+ self.map.swap_remove(key)
+ }
+
+ /// Remove and return the key-value pair.
+ ///
+ /// Like [`Vec::swap_remove`], the entry is removed by swapping it with the
+ /// last element of the map and popping it off. This perturbs the position
+ /// of what used to be the last element!
+ #[cfg(feature = "preserve_order")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))]
+ #[inline]
+ pub fn swap_remove_entry<Q>(&mut self, key: &Q) -> Option<(String, Value)>
+ where
+ String: Borrow<Q>,
+ Q: ?Sized + Ord + Eq + Hash,
+ {
+ self.map.swap_remove_entry(key)
+ }
+
+ /// Removes and returns the value corresponding to the key from the map.
+ ///
+ /// Like [`Vec::remove`], the entry is removed by shifting all of the
+ /// elements that follow it, preserving their relative order. This perturbs
+ /// the index of all of those elements!
+ #[cfg(feature = "preserve_order")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))]
+ #[inline]
+ pub fn shift_remove<Q>(&mut self, key: &Q) -> Option<Value>
+ where
+ String: Borrow<Q>,
+ Q: ?Sized + Ord + Eq + Hash,
+ {
+ self.map.shift_remove(key)
+ }
+
+ /// Remove and return the key-value pair.
+ ///
+ /// Like [`Vec::remove`], the entry is removed by shifting all of the
+ /// elements that follow it, preserving their relative order. This perturbs
+ /// the index of all of those elements!
+ #[cfg(feature = "preserve_order")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))]
+ #[inline]
+ pub fn shift_remove_entry<Q>(&mut self, key: &Q) -> Option<(String, Value)>
+ where
+ String: Borrow<Q>,
+ Q: ?Sized + Ord + Eq + Hash,
+ {
+ self.map.shift_remove_entry(key)
}
/// Moves all elements from other into self, leaving other empty.
@@ -276,7 +318,6 @@ impl Map<String, Value> {
///
/// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)`
/// returns `false`.
- #[cfg(not(no_btreemap_retain))]
#[inline]
pub fn retain<F>(&mut self, f: F)
where
diff --git a/src/number.rs b/src/number.rs
index 21a7641..b0231a8 100644
--- a/src/number.rs
+++ b/src/number.rs
@@ -279,6 +279,62 @@ impl Number {
}
}
+ /// Returns the exact original JSON representation that this Number was
+ /// parsed from.
+ ///
+ /// For numbers constructed not via parsing, such as by `From<i32>`, returns
+ /// the JSON representation that serde\_json would serialize for this
+ /// number.
+ ///
+ /// ```
+ /// # use serde_json::Number;
+ /// for value in [
+ /// "7",
+ /// "12.34",
+ /// "34e-56789",
+ /// "0.0123456789000000012345678900000001234567890000123456789",
+ /// "343412345678910111213141516171819202122232425262728293034",
+ /// "-343412345678910111213141516171819202122232425262728293031",
+ /// ] {
+ /// let number: Number = serde_json::from_str(value).unwrap();
+ /// assert_eq!(number.as_str(), value);
+ /// }
+ /// ```
+ #[cfg(feature = "arbitrary_precision")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "arbitrary_precision")))]
+ pub fn as_str(&self) -> &str {
+ &self.n
+ }
+
+ pub(crate) fn as_f32(&self) -> Option<f32> {
+ #[cfg(not(feature = "arbitrary_precision"))]
+ match self.n {
+ N::PosInt(n) => Some(n as f32),
+ N::NegInt(n) => Some(n as f32),
+ N::Float(n) => Some(n as f32),
+ }
+ #[cfg(feature = "arbitrary_precision")]
+ self.n.parse::<f32>().ok().filter(|float| float.is_finite())
+ }
+
+ pub(crate) fn from_f32(f: f32) -> Option<Number> {
+ if f.is_finite() {
+ let n = {
+ #[cfg(not(feature = "arbitrary_precision"))]
+ {
+ N::Float(f as f64)
+ }
+ #[cfg(feature = "arbitrary_precision")]
+ {
+ ryu::Buffer::new().format_finite(f).to_owned()
+ }
+ };
+ Some(Number { n })
+ } else {
+ None
+ }
+ }
+
#[cfg(feature = "arbitrary_precision")]
/// Not public API. Only tests use this.
#[doc(hidden)]
@@ -332,8 +388,8 @@ impl Serialize for Number {
{
use serde::ser::SerializeStruct;
- let mut s = serializer.serialize_struct(TOKEN, 1)?;
- s.serialize_field(TOKEN, &self.n)?;
+ let mut s = tri!(serializer.serialize_struct(TOKEN, 1));
+ tri!(s.serialize_field(TOKEN, &self.n));
s.end()
}
}
@@ -377,11 +433,11 @@ impl<'de> Deserialize<'de> for Number {
where
V: de::MapAccess<'de>,
{
- let value = visitor.next_key::<NumberKey>()?;
+ let value = tri!(visitor.next_key::<NumberKey>());
if value.is_none() {
return Err(de::Error::invalid_type(Unexpected::Map, &self));
}
- let v: NumberFromString = visitor.next_value()?;
+ let v: NumberFromString = tri!(visitor.next_value());
Ok(v.value)
}
}
@@ -420,7 +476,7 @@ impl<'de> de::Deserialize<'de> for NumberKey {
}
}
- deserializer.deserialize_identifier(FieldVisitor)?;
+ tri!(deserializer.deserialize_identifier(FieldVisitor));
Ok(NumberKey)
}
}
@@ -523,7 +579,7 @@ macro_rules! deserialize_number {
where
V: de::Visitor<'de>,
{
- visitor.$visit(self.n.parse().map_err(|_| invalid_number())?)
+ visitor.$visit(tri!(self.n.parse().map_err(|_| invalid_number())))
}
};
}
diff --git a/src/raw.rs b/src/raw.rs
index 6aa4ffc..a2bf0ec 100644
--- a/src/raw.rs
+++ b/src/raw.rs
@@ -177,11 +177,9 @@ impl RawValue {
/// - the input has no leading or trailing whitespace, and
/// - the input has capacity equal to its length.
pub fn from_string(json: String) -> Result<Box<Self>, Error> {
- {
- let borrowed = crate::from_str::<&Self>(&json)?;
- if borrowed.json.len() < json.len() {
- return Ok(borrowed.to_owned());
- }
+ let borrowed = tri!(crate::from_str::<&Self>(&json));
+ if borrowed.json.len() < json.len() {
+ return Ok(borrowed.to_owned());
}
Ok(Self::from_owned(json.into_boxed_str()))
}
@@ -287,7 +285,7 @@ pub fn to_raw_value<T>(value: &T) -> Result<Box<RawValue>, Error>
where
T: ?Sized + Serialize,
{
- let json_string = crate::to_string(value)?;
+ let json_string = tri!(crate::to_string(value));
Ok(RawValue::from_owned(json_string.into_boxed_str()))
}
@@ -298,8 +296,8 @@ impl Serialize for RawValue {
where
S: Serializer,
{
- let mut s = serializer.serialize_struct(TOKEN, 1)?;
- s.serialize_field(TOKEN, &self.json)?;
+ let mut s = tri!(serializer.serialize_struct(TOKEN, 1));
+ tri!(s.serialize_field(TOKEN, &self.json));
s.end()
}
}
@@ -322,7 +320,7 @@ impl<'de: 'a, 'a> Deserialize<'de> for &'a RawValue {
where
V: MapAccess<'de>,
{
- let value = visitor.next_key::<RawKey>()?;
+ let value = tri!(visitor.next_key::<RawKey>());
if value.is_none() {
return Err(de::Error::invalid_type(Unexpected::Map, &self));
}
@@ -352,7 +350,7 @@ impl<'de> Deserialize<'de> for Box<RawValue> {
where
V: MapAccess<'de>,
{
- let value = visitor.next_key::<RawKey>()?;
+ let value = tri!(visitor.next_key::<RawKey>());
if value.is_none() {
return Err(de::Error::invalid_type(Unexpected::Map, &self));
}
@@ -392,7 +390,7 @@ impl<'de> Deserialize<'de> for RawKey {
}
}
- deserializer.deserialize_identifier(FieldVisitor)?;
+ tri!(deserializer.deserialize_identifier(FieldVisitor));
Ok(RawKey)
}
}
@@ -529,3 +527,251 @@ impl<'de> MapAccess<'de> for BorrowedRawDeserializer<'de> {
seed.deserialize(BorrowedStrDeserializer::new(self.raw_value.take().unwrap()))
}
}
+
+impl<'de> IntoDeserializer<'de, Error> for &'de RawValue {
+ type Deserializer = &'de RawValue;
+
+ fn into_deserializer(self) -> Self::Deserializer {
+ self
+ }
+}
+
+impl<'de> Deserializer<'de> for &'de RawValue {
+ type Error = Error;
+
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_any(visitor)
+ }
+
+ fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_bool(visitor)
+ }
+
+ fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_i8(visitor)
+ }
+
+ fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_i16(visitor)
+ }
+
+ fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_i32(visitor)
+ }
+
+ fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_i64(visitor)
+ }
+
+ fn deserialize_i128<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_i128(visitor)
+ }
+
+ fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_u8(visitor)
+ }
+
+ fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_u16(visitor)
+ }
+
+ fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_u32(visitor)
+ }
+
+ fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_u64(visitor)
+ }
+
+ fn deserialize_u128<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_u128(visitor)
+ }
+
+ fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_f32(visitor)
+ }
+
+ fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_f64(visitor)
+ }
+
+ fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_char(visitor)
+ }
+
+ fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_str(visitor)
+ }
+
+ fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_string(visitor)
+ }
+
+ fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_bytes(visitor)
+ }
+
+ fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_byte_buf(visitor)
+ }
+
+ fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_option(visitor)
+ }
+
+ fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_unit(visitor)
+ }
+
+ fn deserialize_unit_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_unit_struct(name, visitor)
+ }
+
+ fn deserialize_newtype_struct<V>(
+ self,
+ name: &'static str,
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_newtype_struct(name, visitor)
+ }
+
+ fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_seq(visitor)
+ }
+
+ fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_tuple(len, visitor)
+ }
+
+ fn deserialize_tuple_struct<V>(
+ self,
+ name: &'static str,
+ len: usize,
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_tuple_struct(name, len, visitor)
+ }
+
+ fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_map(visitor)
+ }
+
+ fn deserialize_struct<V>(
+ self,
+ name: &'static str,
+ fields: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_struct(name, fields, visitor)
+ }
+
+ fn deserialize_enum<V>(
+ self,
+ name: &'static str,
+ variants: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_enum(name, variants, visitor)
+ }
+
+ fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_identifier(visitor)
+ }
+
+ fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ crate::Deserializer::from_str(&self.json).deserialize_ignored_any(visitor)
+ }
+}
diff --git a/src/read.rs b/src/read.rs
index fc3a3ca..7446f28 100644
--- a/src/read.rs
+++ b/src/read.rs
@@ -81,7 +81,7 @@ pub trait Read<'de>: private::Sealed {
#[doc(hidden)]
fn ignore_str(&mut self) -> Result<()>;
- /// Assumes the previous byte was a hex escape sequnce ('\u') in a string.
+ /// Assumes the previous byte was a hex escape sequence ('\u') in a string.
/// Parses next hexadecimal sequence.
#[doc(hidden)]
fn decode_hex_escape(&mut self) -> Result<u16>;
diff --git a/src/ser.rs b/src/ser.rs
index 80c2deb..3742e0b 100644
--- a/src/ser.rs
+++ b/src/ser.rs
@@ -189,12 +189,9 @@ where
#[inline]
fn serialize_bytes(self, value: &[u8]) -> Result<()> {
- use serde::ser::SerializeSeq;
- let mut seq = tri!(self.serialize_seq(Some(value.len())));
- for byte in value {
- tri!(seq.serialize_element(byte));
- }
- seq.end()
+ self.formatter
+ .write_byte_array(&mut self.writer, value)
+ .map_err(Error::io)
}
#[inline]
@@ -439,17 +436,15 @@ where
.formatter
.begin_string(&mut self.writer)
.map_err(Error::io));
- {
- let mut adapter = Adapter {
- writer: &mut self.writer,
- formatter: &mut self.formatter,
- error: None,
- };
- match write!(adapter, "{}", value) {
- Ok(()) => debug_assert!(adapter.error.is_none()),
- Err(fmt::Error) => {
- return Err(Error::io(adapter.error.expect("there should be an error")));
- }
+ let mut adapter = Adapter {
+ writer: &mut self.writer,
+ formatter: &mut self.formatter,
+ error: None,
+ };
+ match write!(adapter, "{}", value) {
+ Ok(()) => debug_assert!(adapter.error.is_none()),
+ Err(fmt::Error) => {
+ return Err(Error::io(adapter.error.expect("there should be an error")));
}
}
self.formatter
@@ -789,6 +784,10 @@ fn key_must_be_a_string() -> Error {
Error::syntax(ErrorCode::KeyMustBeAString, 0, 0)
}
+fn float_key_must_be_finite() -> Error {
+ Error::syntax(ErrorCode::FloatKeyMustBeFinite, 0, 0)
+}
+
impl<'a, W, F> ser::Serializer for MapKeySerializer<'a, W, F>
where
W: io::Write,
@@ -828,8 +827,21 @@ where
type SerializeStruct = Impossible<(), Error>;
type SerializeStructVariant = Impossible<(), Error>;
- fn serialize_bool(self, _value: bool) -> Result<()> {
- Err(key_must_be_a_string())
+ fn serialize_bool(self, value: bool) -> Result<()> {
+ tri!(self
+ .ser
+ .formatter
+ .begin_string(&mut self.ser.writer)
+ .map_err(Error::io));
+ tri!(self
+ .ser
+ .formatter
+ .write_bool(&mut self.ser.writer, value)
+ .map_err(Error::io));
+ self.ser
+ .formatter
+ .end_string(&mut self.ser.writer)
+ .map_err(Error::io)
}
fn serialize_i8(self, value: i8) -> Result<()> {
@@ -1002,12 +1014,46 @@ where
.map_err(Error::io)
}
- fn serialize_f32(self, _value: f32) -> Result<()> {
- Err(key_must_be_a_string())
+ fn serialize_f32(self, value: f32) -> Result<()> {
+ if !value.is_finite() {
+ return Err(float_key_must_be_finite());
+ }
+
+ tri!(self
+ .ser
+ .formatter
+ .begin_string(&mut self.ser.writer)
+ .map_err(Error::io));
+ tri!(self
+ .ser
+ .formatter
+ .write_f32(&mut self.ser.writer, value)
+ .map_err(Error::io));
+ self.ser
+ .formatter
+ .end_string(&mut self.ser.writer)
+ .map_err(Error::io)
}
- fn serialize_f64(self, _value: f64) -> Result<()> {
- Err(key_must_be_a_string())
+ fn serialize_f64(self, value: f64) -> Result<()> {
+ if !value.is_finite() {
+ return Err(float_key_must_be_finite());
+ }
+
+ tri!(self
+ .ser
+ .formatter
+ .begin_string(&mut self.ser.writer)
+ .map_err(Error::io));
+ tri!(self
+ .ser
+ .formatter
+ .write_f64(&mut self.ser.writer, value)
+ .map_err(Error::io));
+ self.ser
+ .formatter
+ .end_string(&mut self.ser.writer)
+ .map_err(Error::io)
}
fn serialize_char(self, value: char) -> Result<()> {
@@ -1043,11 +1089,11 @@ where
Err(key_must_be_a_string())
}
- fn serialize_some<T>(self, _value: &T) -> Result<()>
+ fn serialize_some<T>(self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
- Err(key_must_be_a_string())
+ value.serialize(self)
}
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
@@ -1734,6 +1780,24 @@ pub trait Formatter {
writer.write_all(s)
}
+ /// Writes the representation of a byte array. Formatters can choose whether
+ /// to represent bytes as a JSON array of integers (the default), or some
+ /// JSON string encoding like hex or base64.
+ fn write_byte_array<W>(&mut self, writer: &mut W, value: &[u8]) -> io::Result<()>
+ where
+ W: ?Sized + io::Write,
+ {
+ tri!(self.begin_array(writer));
+ let mut first = true;
+ for byte in value {
+ tri!(self.begin_array_value(writer, first));
+ tri!(self.write_u8(writer, *byte));
+ tri!(self.end_array_value(writer));
+ first = false;
+ }
+ self.end_array(writer)
+ }
+
/// Called before every array. Writes a `[` to the specified
/// writer.
#[inline]
@@ -2062,7 +2126,9 @@ static ESCAPE: [u8; 256] = [
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F
];
-/// Serialize the given data structure as JSON into the IO stream.
+/// Serialize the given data structure as JSON into the I/O stream.
+///
+/// Serialization guarantees it only feeds valid UTF-8 sequences to the writer.
///
/// # Errors
///
@@ -2079,9 +2145,11 @@ where
value.serialize(&mut ser)
}
-/// Serialize the given data structure as pretty-printed JSON into the IO
+/// Serialize the given data structure as pretty-printed JSON into the I/O
/// stream.
///
+/// Serialization guarantees it only feeds valid UTF-8 sequences to the writer.
+///
/// # Errors
///
/// Serialization can fail if `T`'s implementation of `Serialize` decides to
diff --git a/src/value/de.rs b/src/value/de.rs
index 9c266d0..1e8b5ac 100644
--- a/src/value/de.rs
+++ b/src/value/de.rs
@@ -1,4 +1,4 @@
-use crate::error::Error;
+use crate::error::{Error, ErrorCode};
use crate::map::Map;
use crate::number::Number;
use crate::value::Value;
@@ -106,15 +106,15 @@ impl<'de> Deserialize<'de> for Value {
where
V: MapAccess<'de>,
{
- match visitor.next_key_seed(KeyClassifier)? {
+ match tri!(visitor.next_key_seed(KeyClassifier)) {
#[cfg(feature = "arbitrary_precision")]
Some(KeyClass::Number) => {
- let number: NumberFromString = visitor.next_value()?;
+ let number: NumberFromString = tri!(visitor.next_value());
Ok(Value::Number(number.value))
}
#[cfg(feature = "raw_value")]
Some(KeyClass::RawValue) => {
- let value = visitor.next_value_seed(crate::raw::BoxedFromString)?;
+ let value = tri!(visitor.next_value_seed(crate::raw::BoxedFromString));
crate::from_str(value.get()).map_err(de::Error::custom)
}
Some(KeyClass::Map(first_key)) => {
@@ -482,6 +482,14 @@ impl<'de> IntoDeserializer<'de, Error> for Value {
}
}
+impl<'de> IntoDeserializer<'de, Error> for &'de Value {
+ type Deserializer = Self;
+
+ fn into_deserializer(self) -> Self::Deserializer {
+ self
+ }
+}
+
struct VariantDeserializer {
value: Option<Value>,
}
@@ -1120,18 +1128,30 @@ struct MapKeyDeserializer<'de> {
key: Cow<'de, str>,
}
-macro_rules! deserialize_integer_key {
- ($method:ident => $visit:ident) => {
+macro_rules! deserialize_numeric_key {
+ ($method:ident) => {
+ deserialize_numeric_key!($method, deserialize_number);
+ };
+
+ ($method:ident, $using:ident) => {
fn $method<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
- match (self.key.parse(), self.key) {
- (Ok(integer), _) => visitor.$visit(integer),
- (Err(_), Cow::Borrowed(s)) => visitor.visit_borrowed_str(s),
- #[cfg(any(feature = "std", feature = "alloc"))]
- (Err(_), Cow::Owned(s)) => visitor.visit_string(s),
+ let mut de = crate::Deserializer::from_str(&self.key);
+
+ match tri!(de.peek()) {
+ Some(b'0'..=b'9' | b'-') => {}
+ _ => return Err(Error::syntax(ErrorCode::ExpectedNumericKey, 0, 0)),
+ }
+
+ let number = tri!(de.$using(visitor));
+
+ if tri!(de.peek()).is_some() {
+ return Err(Error::syntax(ErrorCode::ExpectedNumericKey, 0, 0));
}
+
+ Ok(number)
}
};
}
@@ -1146,16 +1166,38 @@ impl<'de> serde::Deserializer<'de> for MapKeyDeserializer<'de> {
BorrowedCowStrDeserializer::new(self.key).deserialize_any(visitor)
}
- deserialize_integer_key!(deserialize_i8 => visit_i8);
- deserialize_integer_key!(deserialize_i16 => visit_i16);
- deserialize_integer_key!(deserialize_i32 => visit_i32);
- deserialize_integer_key!(deserialize_i64 => visit_i64);
- deserialize_integer_key!(deserialize_i128 => visit_i128);
- deserialize_integer_key!(deserialize_u8 => visit_u8);
- deserialize_integer_key!(deserialize_u16 => visit_u16);
- deserialize_integer_key!(deserialize_u32 => visit_u32);
- deserialize_integer_key!(deserialize_u64 => visit_u64);
- deserialize_integer_key!(deserialize_u128 => visit_u128);
+ deserialize_numeric_key!(deserialize_i8);
+ deserialize_numeric_key!(deserialize_i16);
+ deserialize_numeric_key!(deserialize_i32);
+ deserialize_numeric_key!(deserialize_i64);
+ deserialize_numeric_key!(deserialize_u8);
+ deserialize_numeric_key!(deserialize_u16);
+ deserialize_numeric_key!(deserialize_u32);
+ deserialize_numeric_key!(deserialize_u64);
+ #[cfg(not(feature = "float_roundtrip"))]
+ deserialize_numeric_key!(deserialize_f32);
+ deserialize_numeric_key!(deserialize_f64);
+
+ #[cfg(feature = "float_roundtrip")]
+ deserialize_numeric_key!(deserialize_f32, do_deserialize_f32);
+ deserialize_numeric_key!(deserialize_i128, do_deserialize_i128);
+ deserialize_numeric_key!(deserialize_u128, do_deserialize_u128);
+
+ fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Error>
+ where
+ V: Visitor<'de>,
+ {
+ if self.key == "true" {
+ visitor.visit_bool(true)
+ } else if self.key == "false" {
+ visitor.visit_bool(false)
+ } else {
+ Err(serde::de::Error::invalid_type(
+ Unexpected::Str(&self.key),
+ &visitor,
+ ))
+ }
+ }
#[inline]
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Error>
@@ -1193,8 +1235,8 @@ impl<'de> serde::Deserializer<'de> for MapKeyDeserializer<'de> {
}
forward_to_deserialize_any! {
- bool f32 f64 char str string bytes byte_buf unit unit_struct seq tuple
- tuple_struct map struct identifier ignored_any
+ char str string bytes byte_buf unit unit_struct seq tuple tuple_struct
+ map struct identifier ignored_any
}
}
@@ -1327,7 +1369,7 @@ impl<'de> de::EnumAccess<'de> for BorrowedCowStrDeserializer<'de> {
where
T: de::DeserializeSeed<'de>,
{
- let value = seed.deserialize(self)?;
+ let value = tri!(seed.deserialize(self));
Ok((value, UnitOnly))
}
}
diff --git a/src/value/from.rs b/src/value/from.rs
index c5a6a39..ed1e333 100644
--- a/src/value/from.rs
+++ b/src/value/from.rs
@@ -4,7 +4,6 @@ use crate::number::Number;
use alloc::borrow::Cow;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
-use core::iter::FromIterator;
macro_rules! from_integer {
($($ty:ident)*) => {
@@ -29,7 +28,8 @@ from_integer! {
}
impl From<f32> for Value {
- /// Convert 32-bit floating point number to `Value`
+ /// Convert 32-bit floating point number to `Value::Number`, or
+ /// `Value::Null` if infinite or NaN.
///
/// # Examples
///
@@ -40,12 +40,13 @@ impl From<f32> for Value {
/// let x: Value = f.into();
/// ```
fn from(f: f32) -> Self {
- From::from(f as f64)
+ Number::from_f32(f).map_or(Value::Null, Value::Number)
}
}
impl From<f64> for Value {
- /// Convert 64-bit floating point number to `Value`
+ /// Convert 64-bit floating point number to `Value::Number`, or
+ /// `Value::Null` if infinite or NaN.
///
/// # Examples
///
@@ -61,7 +62,7 @@ impl From<f64> for Value {
}
impl From<bool> for Value {
- /// Convert boolean to `Value`
+ /// Convert boolean to `Value::Bool`.
///
/// # Examples
///
@@ -77,7 +78,7 @@ impl From<bool> for Value {
}
impl From<String> for Value {
- /// Convert `String` to `Value`
+ /// Convert `String` to `Value::String`.
///
/// # Examples
///
@@ -92,8 +93,8 @@ impl From<String> for Value {
}
}
-impl<'a> From<&'a str> for Value {
- /// Convert string slice to `Value`
+impl From<&str> for Value {
+ /// Convert string slice to `Value::String`.
///
/// # Examples
///
@@ -109,7 +110,7 @@ impl<'a> From<&'a str> for Value {
}
impl<'a> From<Cow<'a, str>> for Value {
- /// Convert copy-on-write string to `Value`
+ /// Convert copy-on-write string to `Value::String`.
///
/// # Examples
///
@@ -134,7 +135,7 @@ impl<'a> From<Cow<'a, str>> for Value {
}
impl From<Number> for Value {
- /// Convert `Number` to `Value`
+ /// Convert `Number` to `Value::Number`.
///
/// # Examples
///
@@ -150,7 +151,7 @@ impl From<Number> for Value {
}
impl From<Map<String, Value>> for Value {
- /// Convert map (with string keys) to `Value`
+ /// Convert map (with string keys) to `Value::Object`.
///
/// # Examples
///
@@ -167,7 +168,7 @@ impl From<Map<String, Value>> for Value {
}
impl<T: Into<Value>> From<Vec<T>> for Value {
- /// Convert a `Vec` to `Value`
+ /// Convert a `Vec` to `Value::Array`.
///
/// # Examples
///
@@ -182,8 +183,8 @@ impl<T: Into<Value>> From<Vec<T>> for Value {
}
}
-impl<'a, T: Clone + Into<Value>> From<&'a [T]> for Value {
- /// Convert a slice to `Value`
+impl<T: Clone + Into<Value>> From<&[T]> for Value {
+ /// Convert a slice to `Value::Array`.
///
/// # Examples
///
@@ -193,13 +194,13 @@ impl<'a, T: Clone + Into<Value>> From<&'a [T]> for Value {
/// let v: &[&str] = &["lorem", "ipsum", "dolor"];
/// let x: Value = v.into();
/// ```
- fn from(f: &'a [T]) -> Self {
+ fn from(f: &[T]) -> Self {
Value::Array(f.iter().cloned().map(Into::into).collect())
}
}
impl<T: Into<Value>> FromIterator<T> for Value {
- /// Convert an iteratable type to a `Value`
+ /// Create a `Value::Array` by collecting an iterator of array elements.
///
/// # Examples
///
@@ -229,7 +230,7 @@ impl<T: Into<Value>> FromIterator<T> for Value {
}
impl<K: Into<String>, V: Into<Value>> FromIterator<(K, V)> for Value {
- /// Convert an iteratable type to a `Value`
+ /// Create a `Value::Object` by collecting an iterator of key-value pairs.
///
/// # Examples
///
@@ -249,7 +250,7 @@ impl<K: Into<String>, V: Into<Value>> FromIterator<(K, V)> for Value {
}
impl From<()> for Value {
- /// Convert `()` to `Value`
+ /// Convert `()` to `Value::Null`.
///
/// # Examples
///
diff --git a/src/value/index.rs b/src/value/index.rs
index c74042b..891ca8e 100644
--- a/src/value/index.rs
+++ b/src/value/index.rs
@@ -116,7 +116,7 @@ impl Index for String {
}
}
-impl<'a, T> Index for &'a T
+impl<T> Index for &T
where
T: ?Sized + Index,
{
diff --git a/src/value/mod.rs b/src/value/mod.rs
index 470b6b2..b3f51ea 100644
--- a/src/value/mod.rs
+++ b/src/value/mod.rs
@@ -106,6 +106,7 @@ pub use crate::map::Map;
pub use crate::number::Number;
#[cfg(feature = "raw_value")]
+#[cfg_attr(docsrs, doc(cfg(feature = "raw_value")))]
pub use crate::raw::{to_raw_value, RawValue};
/// Represents any valid JSON value.
@@ -182,11 +183,11 @@ impl Debug for Value {
Value::Number(number) => Debug::fmt(number, formatter),
Value::String(string) => write!(formatter, "String({:?})", string),
Value::Array(vec) => {
- formatter.write_str("Array ")?;
+ tri!(formatter.write_str("Array "));
Debug::fmt(vec, formatter)
}
Value::Object(map) => {
- formatter.write_str("Object ")?;
+ tri!(formatter.write_str("Object "));
Debug::fmt(map, formatter)
}
}
@@ -514,6 +515,28 @@ impl Value {
}
}
+ /// If the `Value` is a Number, returns the associated [`Number`]. Returns
+ /// None otherwise.
+ ///
+ /// ```
+ /// # use serde_json::{json, Number};
+ /// #
+ /// let v = json!({ "a": 1, "b": 2.2, "c": -3, "d": "4" });
+ ///
+ /// assert_eq!(v["a"].as_number(), Some(&Number::from(1u64)));
+ /// assert_eq!(v["b"].as_number(), Some(&Number::from_f64(2.2).unwrap()));
+ /// assert_eq!(v["c"].as_number(), Some(&Number::from(-3i64)));
+ ///
+ /// // The string `"4"` is not a number.
+ /// assert_eq!(v["d"].as_number(), None);
+ /// ```
+ pub fn as_number(&self) -> Option<&Number> {
+ match self {
+ Value::Number(number) => Some(number),
+ _ => None,
+ }
+ }
+
/// Returns true if the `Value` is an integer between `i64::MIN` and
/// `i64::MAX`.
///
@@ -889,7 +912,6 @@ mod ser;
/// ```
/// use serde::Serialize;
/// use serde_json::json;
-///
/// use std::error::Error;
///
/// #[derive(Serialize)]
@@ -898,7 +920,7 @@ mod ser;
/// location: String,
/// }
///
-/// fn compare_json_values() -> Result<(), Box<Error>> {
+/// fn compare_json_values() -> Result<(), Box<dyn Error>> {
/// let u = User {
/// fingerprint: "0xF9BA143B95FF6D82".to_owned(),
/// location: "Menlo Park, CA".to_owned(),
diff --git a/src/value/partial_eq.rs b/src/value/partial_eq.rs
index b4ef84c..46c1dbc 100644
--- a/src/value/partial_eq.rs
+++ b/src/value/partial_eq.rs
@@ -9,6 +9,13 @@ fn eq_u64(value: &Value, other: u64) -> bool {
value.as_u64().map_or(false, |i| i == other)
}
+fn eq_f32(value: &Value, other: f32) -> bool {
+ match value {
+ Value::Number(n) => n.as_f32().map_or(false, |i| i == other),
+ _ => false,
+ }
+}
+
fn eq_f64(value: &Value, other: f64) -> bool {
value.as_f64().map_or(false, |i| i == other)
}
@@ -27,7 +34,7 @@ impl PartialEq<str> for Value {
}
}
-impl<'a> PartialEq<&'a str> for Value {
+impl PartialEq<&str> for Value {
fn eq(&self, other: &&str) -> bool {
eq_str(self, *other)
}
@@ -39,7 +46,7 @@ impl PartialEq<Value> for str {
}
}
-impl<'a> PartialEq<Value> for &'a str {
+impl PartialEq<Value> for &str {
fn eq(&self, other: &Value) -> bool {
eq_str(other, *self)
}
@@ -90,6 +97,7 @@ macro_rules! partialeq_numeric {
partialeq_numeric! {
eq_i64[i8 i16 i32 i64 isize]
eq_u64[u8 u16 u32 u64 usize]
- eq_f64[f32 f64]
+ eq_f32[f32]
+ eq_f64[f64]
eq_bool[bool]
}
diff --git a/src/value/ser.rs b/src/value/ser.rs
index a29814e..835fa90 100644
--- a/src/value/ser.rs
+++ b/src/value/ser.rs
@@ -1,12 +1,9 @@
use crate::error::{Error, ErrorCode, Result};
use crate::map::Map;
-use crate::number::Number;
use crate::value::{to_value, Value};
use alloc::borrow::ToOwned;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
-#[cfg(not(feature = "arbitrary_precision"))]
-use core::convert::TryFrom;
use core::fmt::Display;
use core::result;
use serde::ser::{Impossible, Serialize};
@@ -149,13 +146,13 @@ impl serde::Serializer for Serializer {
}
#[inline]
- fn serialize_f32(self, value: f32) -> Result<Value> {
- self.serialize_f64(value as f64)
+ fn serialize_f32(self, float: f32) -> Result<Value> {
+ Ok(Value::from(float))
}
#[inline]
- fn serialize_f64(self, value: f64) -> Result<Value> {
- Ok(Number::from_f64(value).map_or(Value::Null, Value::Number))
+ fn serialize_f64(self, float: f64) -> Result<Value> {
+ Ok(Value::from(float))
}
#[inline]
@@ -452,6 +449,10 @@ fn key_must_be_a_string() -> Error {
Error::syntax(ErrorCode::KeyMustBeAString, 0, 0)
}
+fn float_key_must_be_finite() -> Error {
+ Error::syntax(ErrorCode::FloatKeyMustBeFinite, 0, 0)
+}
+
impl serde::Serializer for MapKeySerializer {
type Ok = String;
type Error = Error;
@@ -482,8 +483,8 @@ impl serde::Serializer for MapKeySerializer {
value.serialize(self)
}
- fn serialize_bool(self, _value: bool) -> Result<String> {
- Err(key_must_be_a_string())
+ fn serialize_bool(self, value: bool) -> Result<String> {
+ Ok(value.to_string())
}
fn serialize_i8(self, value: i8) -> Result<String> {
@@ -518,12 +519,20 @@ impl serde::Serializer for MapKeySerializer {
Ok(value.to_string())
}
- fn serialize_f32(self, _value: f32) -> Result<String> {
- Err(key_must_be_a_string())
+ fn serialize_f32(self, value: f32) -> Result<String> {
+ if value.is_finite() {
+ Ok(ryu::Buffer::new().format_finite(value).to_owned())
+ } else {
+ Err(float_key_must_be_finite())
+ }
}
- fn serialize_f64(self, _value: f64) -> Result<String> {
- Err(key_must_be_a_string())
+ fn serialize_f64(self, value: f64) -> Result<String> {
+ if value.is_finite() {
+ Ok(ryu::Buffer::new().format_finite(value).to_owned())
+ } else {
+ Err(float_key_must_be_finite())
+ }
}
#[inline]
@@ -641,7 +650,7 @@ impl serde::ser::SerializeStruct for SerializeMap {
#[cfg(feature = "arbitrary_precision")]
SerializeMap::Number { out_value } => {
if key == crate::number::TOKEN {
- *out_value = Some(value.serialize(NumberValueEmitter)?);
+ *out_value = Some(tri!(value.serialize(NumberValueEmitter)));
Ok(())
} else {
Err(invalid_number())
@@ -650,7 +659,7 @@ impl serde::ser::SerializeStruct for SerializeMap {
#[cfg(feature = "raw_value")]
SerializeMap::RawValue { out_value } => {
if key == crate::raw::TOKEN {
- *out_value = Some(value.serialize(RawValueEmitter)?);
+ *out_value = Some(tri!(value.serialize(RawValueEmitter)));
Ok(())
} else {
Err(invalid_raw_value())
diff --git a/tests/lexical.rs b/tests/lexical.rs
index d3dfb85..368c844 100644
--- a/tests/lexical.rs
+++ b/tests/lexical.rs
@@ -26,11 +26,6 @@ extern crate alloc;
#[path = "../src/lexical/mod.rs"]
mod lexical;
-mod lib {
- pub use std::vec::Vec;
- pub use std::{cmp, iter, mem, ops};
-}
-
#[path = "lexical/algorithm.rs"]
mod algorithm;
diff --git a/tests/lexical/parse.rs b/tests/lexical/parse.rs
index 80ca25e..03ec1a9 100644
--- a/tests/lexical/parse.rs
+++ b/tests/lexical/parse.rs
@@ -1,7 +1,7 @@
// Adapted from https://github.com/Alexhuszagh/rust-lexical.
use crate::lexical::num::Float;
-use crate::lexical::parse::{parse_concise_float, parse_truncated_float};
+use crate::lexical::{parse_concise_float, parse_truncated_float};
use core::f64;
use core::fmt::Debug;
diff --git a/tests/map.rs b/tests/map.rs
index ae01969..538cd16 100644
--- a/tests/map.rs
+++ b/tests/map.rs
@@ -35,7 +35,6 @@ fn test_append() {
assert!(val.is_empty());
}
-#[cfg(not(no_btreemap_retain))]
#[test]
fn test_retain() {
let mut v: Value = from_str(r#"{"b":null,"a":null,"c":null}"#).unwrap();
diff --git a/tests/regression/issue1004.rs b/tests/regression/issue1004.rs
new file mode 100644
index 0000000..c09fb96
--- /dev/null
+++ b/tests/regression/issue1004.rs
@@ -0,0 +1,12 @@
+#![cfg(feature = "arbitrary_precision")]
+
+#[test]
+fn test() {
+ let float = 5.55f32;
+ let value = serde_json::to_value(float).unwrap();
+ let json = serde_json::to_string(&value).unwrap();
+
+ // If the f32 were cast to f64 by Value before serialization, then this
+ // would incorrectly serialize as 5.550000190734863.
+ assert_eq!(json, "5.55");
+}
diff --git a/tests/regression/issue845.rs b/tests/regression/issue845.rs
index 56037ae..e8b0c0f 100644
--- a/tests/regression/issue845.rs
+++ b/tests/regression/issue845.rs
@@ -1,7 +1,6 @@
#![allow(clippy::trait_duplication_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/8757
use serde::{Deserialize, Deserializer};
-use std::convert::TryFrom;
use std::fmt::{self, Display};
use std::marker::PhantomData;
use std::str::FromStr;
diff --git a/tests/test.rs b/tests/test.rs
index 6c08cc8..05b7f86 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -14,9 +14,6 @@
clippy::vec_init_then_push,
clippy::zero_sized_map_values
)]
-#![cfg_attr(feature = "trace-macros", feature(trace_macros))]
-#[cfg(feature = "trace-macros")]
-trace_macros!(true);
#[macro_use]
mod macros;
@@ -33,11 +30,12 @@ use serde_json::{
from_reader, from_slice, from_str, from_value, json, to_string, to_string_pretty, to_value,
to_vec, Deserializer, Number, Value,
};
-use std::collections::hash_map::DefaultHasher;
use std::collections::BTreeMap;
#[cfg(feature = "raw_value")]
use std::collections::HashMap;
use std::fmt::{self, Debug};
+use std::hash::BuildHasher;
+#[cfg(feature = "raw_value")]
use std::hash::{Hash, Hasher};
use std::io;
use std::iter;
@@ -53,7 +51,7 @@ macro_rules! treemap {
() => {
BTreeMap::new()
};
- ($($k:expr => $v:expr),+) => {
+ ($($k:expr => $v:expr),+ $(,)?) => {
{
let mut m = BTreeMap::new();
$(
@@ -160,17 +158,29 @@ fn test_write_f64() {
#[test]
fn test_encode_nonfinite_float_yields_null() {
- let v = to_value(::std::f64::NAN).unwrap();
+ let v = to_value(::std::f64::NAN.copysign(1.0)).unwrap();
+ assert!(v.is_null());
+
+ let v = to_value(::std::f64::NAN.copysign(-1.0)).unwrap();
assert!(v.is_null());
let v = to_value(::std::f64::INFINITY).unwrap();
assert!(v.is_null());
- let v = to_value(::std::f32::NAN).unwrap();
+ let v = to_value(-::std::f64::INFINITY).unwrap();
+ assert!(v.is_null());
+
+ let v = to_value(::std::f32::NAN.copysign(1.0)).unwrap();
+ assert!(v.is_null());
+
+ let v = to_value(::std::f32::NAN.copysign(-1.0)).unwrap();
assert!(v.is_null());
let v = to_value(::std::f32::INFINITY).unwrap();
assert!(v.is_null());
+
+ let v = to_value(-::std::f32::INFINITY).unwrap();
+ assert!(v.is_null());
}
#[test]
@@ -264,7 +274,7 @@ fn test_write_object() {
(
treemap!(
"a".to_string() => true,
- "b".to_string() => false
+ "b".to_string() => false,
),
"{\"a\":true,\"b\":false}",
),
@@ -275,7 +285,7 @@ fn test_write_object() {
treemap![
"a".to_string() => treemap![],
"b".to_string() => treemap![],
- "c".to_string() => treemap![]
+ "c".to_string() => treemap![],
],
"{\"a\":{},\"b\":{},\"c\":{}}",
),
@@ -284,10 +294,10 @@ fn test_write_object() {
"a".to_string() => treemap![
"a".to_string() => treemap!["a" => vec![1,2,3]],
"b".to_string() => treemap![],
- "c".to_string() => treemap![]
+ "c".to_string() => treemap![],
],
"b".to_string() => treemap![],
- "c".to_string() => treemap![]
+ "c".to_string() => treemap![],
],
"{\"a\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}},\"b\":{},\"c\":{}}",
),
@@ -297,9 +307,9 @@ fn test_write_object() {
"b".to_string() => treemap![
"a".to_string() => treemap!["a" => vec![1,2,3]],
"b".to_string() => treemap![],
- "c".to_string() => treemap![]
+ "c".to_string() => treemap![],
],
- "c".to_string() => treemap![]
+ "c".to_string() => treemap![],
],
"{\"a\":{},\"b\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}},\"c\":{}}",
),
@@ -310,8 +320,8 @@ fn test_write_object() {
"c".to_string() => treemap![
"a".to_string() => treemap!["a" => vec![1,2,3]],
"b".to_string() => treemap![],
- "c".to_string() => treemap![]
- ]
+ "c".to_string() => treemap![],
+ ],
],
"{\"a\":{},\"b\":{},\"c\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}}}",
),
@@ -324,7 +334,7 @@ fn test_write_object() {
treemap![
"a".to_string() => treemap![],
"b".to_string() => treemap![],
- "c".to_string() => treemap![]
+ "c".to_string() => treemap![],
],
pretty_str!({
"a": {},
@@ -337,10 +347,10 @@ fn test_write_object() {
"a".to_string() => treemap![
"a".to_string() => treemap!["a" => vec![1,2,3]],
"b".to_string() => treemap![],
- "c".to_string() => treemap![]
+ "c".to_string() => treemap![],
],
"b".to_string() => treemap![],
- "c".to_string() => treemap![]
+ "c".to_string() => treemap![],
],
pretty_str!({
"a": {
@@ -364,9 +374,9 @@ fn test_write_object() {
"b".to_string() => treemap![
"a".to_string() => treemap!["a" => vec![1,2,3]],
"b".to_string() => treemap![],
- "c".to_string() => treemap![]
+ "c".to_string() => treemap![],
],
- "c".to_string() => treemap![]
+ "c".to_string() => treemap![],
],
pretty_str!({
"a": {},
@@ -391,8 +401,8 @@ fn test_write_object() {
"c".to_string() => treemap![
"a".to_string() => treemap!["a" => vec![1,2,3]],
"b".to_string() => treemap![],
- "c".to_string() => treemap![]
- ]
+ "c".to_string() => treemap![],
+ ],
],
pretty_str!({
"a": {},
@@ -423,7 +433,7 @@ fn test_write_object() {
(
treemap!(
"a".to_string() => true,
- "b".to_string() => false
+ "b".to_string() => false,
),
pretty_str!( {
"a": true,
@@ -1192,8 +1202,8 @@ fn test_parse_object() {
treemap!(
"a".to_string() => treemap!(
"b".to_string() => 3u64,
- "c".to_string() => 4
- )
+ "c".to_string() => 4,
+ ),
),
)]);
@@ -1369,7 +1379,7 @@ fn test_parse_enum() {
),
treemap!(
"a".to_string() => Animal::Dog,
- "b".to_string() => Animal::Frog("Henry".to_string(), vec![])
+ "b".to_string() => Animal::Frog("Henry".to_string(), vec![]),
),
)]);
}
@@ -1452,7 +1462,6 @@ fn test_serialize_seq_with_no_len() {
where
T: ser::Serialize,
{
- #[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@@ -1479,7 +1488,6 @@ fn test_serialize_seq_with_no_len() {
formatter.write_str("array")
}
- #[inline]
fn visit_unit<E>(self) -> Result<MyVec<T>, E>
where
E: de::Error,
@@ -1487,7 +1495,6 @@ fn test_serialize_seq_with_no_len() {
Ok(MyVec(Vec::new()))
}
- #[inline]
fn visit_seq<V>(self, mut visitor: V) -> Result<MyVec<T>, V::Error>
where
V: de::SeqAccess<'de>,
@@ -1538,7 +1545,6 @@ fn test_serialize_map_with_no_len() {
K: ser::Serialize + Ord,
V: ser::Serialize,
{
- #[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@@ -1566,7 +1572,6 @@ fn test_serialize_map_with_no_len() {
formatter.write_str("map")
}
- #[inline]
fn visit_unit<E>(self) -> Result<MyMap<K, V>, E>
where
E: de::Error,
@@ -1574,7 +1579,6 @@ fn test_serialize_map_with_no_len() {
Ok(MyMap(BTreeMap::new()))
}
- #[inline]
fn visit_map<Visitor>(self, mut visitor: Visitor) -> Result<MyMap<K, V>, Visitor::Error>
where
Visitor: de::MapAccess<'de>,
@@ -1661,22 +1665,11 @@ fn test_deserialize_from_stream() {
}
#[test]
-fn test_serialize_rejects_bool_keys() {
- let map = treemap!(
- true => 2,
- false => 4
- );
-
- let err = to_vec(&map).unwrap_err();
- assert_eq!(err.to_string(), "key must be a string");
-}
-
-#[test]
fn test_serialize_rejects_adt_keys() {
let map = treemap!(
Some("a") => 2,
Some("b") => 4,
- None => 6
+ None => 6,
);
let err = to_vec(&map).unwrap_err();
@@ -1890,23 +1883,41 @@ fn test_integer_key() {
// map with integer keys
let map = treemap!(
1 => 2,
- -1 => 6
+ -1 => 6,
);
let j = r#"{"-1":6,"1":2}"#;
test_encode_ok(&[(&map, j)]);
test_parse_ok(vec![(j, map)]);
- let j = r#"{"x":null}"#;
- test_parse_err::<BTreeMap<i32, ()>>(&[(
- j,
- "invalid type: string \"x\", expected i32 at line 1 column 4",
- )]);
+ test_parse_err::<BTreeMap<i32, ()>>(&[
+ (
+ r#"{"x":null}"#,
+ "invalid value: expected key to be a number in quotes at line 1 column 2",
+ ),
+ (
+ r#"{" 123":null}"#,
+ "invalid value: expected key to be a number in quotes at line 1 column 2",
+ ),
+ (r#"{"123 ":null}"#, "expected `\"` at line 1 column 6"),
+ ]);
+
+ let err = from_value::<BTreeMap<i32, ()>>(json!({" 123":null})).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "invalid value: expected key to be a number in quotes",
+ );
+
+ let err = from_value::<BTreeMap<i32, ()>>(json!({"123 ":null})).unwrap_err();
+ assert_eq!(
+ err.to_string(),
+ "invalid value: expected key to be a number in quotes",
+ );
}
#[test]
fn test_integer128_key() {
let map = treemap! {
- 100000000000000000000000000000000000000u128 => ()
+ 100000000000000000000000000000000000000u128 => (),
};
let j = r#"{"100000000000000000000000000000000000000":null}"#;
assert_eq!(to_string(&map).unwrap(), j);
@@ -1914,24 +1925,107 @@ fn test_integer128_key() {
}
#[test]
-fn test_deny_float_key() {
- #[derive(Eq, PartialEq, Ord, PartialOrd)]
+fn test_float_key() {
+ #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone)]
struct Float;
impl Serialize for Float {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
- serializer.serialize_f32(1.0)
+ serializer.serialize_f32(1.23)
+ }
+ }
+ impl<'de> Deserialize<'de> for Float {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ f32::deserialize(deserializer).map(|_| Float)
}
}
// map with float key
- let map = treemap!(Float => "x");
+ let map = treemap!(Float => "x".to_owned());
+ let j = r#"{"1.23":"x"}"#;
+
+ test_encode_ok(&[(&map, j)]);
+ test_parse_ok(vec![(j, map)]);
+
+ let j = r#"{"x": null}"#;
+ test_parse_err::<BTreeMap<Float, ()>>(&[(
+ j,
+ "invalid value: expected key to be a number in quotes at line 1 column 2",
+ )]);
+}
+
+#[test]
+fn test_deny_non_finite_f32_key() {
+ // We store float bits so that we can derive Ord, and other traits. In a
+ // real context the code might involve a crate like ordered-float.
+
+ #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone)]
+ struct F32Bits(u32);
+ impl Serialize for F32Bits {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_f32(f32::from_bits(self.0))
+ }
+ }
+
+ let map = treemap!(F32Bits(f32::INFINITY.to_bits()) => "x".to_owned());
+ assert!(serde_json::to_string(&map).is_err());
+ assert!(serde_json::to_value(map).is_err());
+
+ let map = treemap!(F32Bits(f32::NEG_INFINITY.to_bits()) => "x".to_owned());
+ assert!(serde_json::to_string(&map).is_err());
+ assert!(serde_json::to_value(map).is_err());
+
+ let map = treemap!(F32Bits(f32::NAN.to_bits()) => "x".to_owned());
+ assert!(serde_json::to_string(&map).is_err());
assert!(serde_json::to_value(map).is_err());
}
#[test]
+fn test_deny_non_finite_f64_key() {
+ // We store float bits so that we can derive Ord, and other traits. In a
+ // real context the code might involve a crate like ordered-float.
+
+ #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone)]
+ struct F64Bits(u64);
+ impl Serialize for F64Bits {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_f64(f64::from_bits(self.0))
+ }
+ }
+
+ let map = treemap!(F64Bits(f64::INFINITY.to_bits()) => "x".to_owned());
+ assert!(serde_json::to_string(&map).is_err());
+ assert!(serde_json::to_value(map).is_err());
+
+ let map = treemap!(F64Bits(f64::NEG_INFINITY.to_bits()) => "x".to_owned());
+ assert!(serde_json::to_string(&map).is_err());
+ assert!(serde_json::to_value(map).is_err());
+
+ let map = treemap!(F64Bits(f64::NAN.to_bits()) => "x".to_owned());
+ assert!(serde_json::to_string(&map).is_err());
+ assert!(serde_json::to_value(map).is_err());
+}
+
+#[test]
+fn test_boolean_key() {
+ let map = treemap!(false => 0, true => 1);
+ let j = r#"{"false":0,"true":1}"#;
+ test_encode_ok(&[(&map, j)]);
+ test_parse_ok(vec![(j, map)]);
+}
+
+#[test]
fn test_borrowed_key() {
let map: BTreeMap<&str, ()> = from_str("{\"borrowed\":null}").unwrap();
let expected = treemap! { "borrowed" => () };
@@ -1954,7 +2048,7 @@ fn test_effectively_string_keys() {
}
let map = treemap! {
Enum::One => 1,
- Enum::Two => 2
+ Enum::Two => 2,
};
let expected = r#"{"One":1,"Two":2}"#;
test_encode_ok(&[(&map, expected)]);
@@ -1964,7 +2058,7 @@ fn test_effectively_string_keys() {
struct Wrapper(String);
let map = treemap! {
Wrapper("zero".to_owned()) => 0,
- Wrapper("one".to_owned()) => 1
+ Wrapper("one".to_owned()) => 1,
};
let expected = r#"{"one":1,"zero":0}"#;
test_encode_ok(&[(&map, expected)]);
@@ -2385,25 +2479,27 @@ fn test_value_into_deserializer() {
let mut map = BTreeMap::new();
map.insert("inner", json!({ "string": "Hello World" }));
+ let outer = Outer::deserialize(serde::de::value::MapDeserializer::new(
+ map.iter().map(|(k, v)| (*k, v)),
+ ))
+ .unwrap();
+ assert_eq!(outer.inner.string, "Hello World");
+
let outer = Outer::deserialize(map.into_deserializer()).unwrap();
assert_eq!(outer.inner.string, "Hello World");
}
#[test]
fn hash_positive_and_negative_zero() {
- fn hash(obj: impl Hash) -> u64 {
- let mut hasher = DefaultHasher::new();
- obj.hash(&mut hasher);
- hasher.finish()
- }
+ let rand = std::hash::RandomState::new();
let k1 = serde_json::from_str::<Number>("0.0").unwrap();
let k2 = serde_json::from_str::<Number>("-0.0").unwrap();
if cfg!(feature = "arbitrary_precision") {
assert_ne!(k1, k2);
- assert_ne!(hash(k1), hash(k2));
+ assert_ne!(rand.hash_one(k1), rand.hash_one(k2));
} else {
assert_eq!(k1, k2);
- assert_eq!(hash(k1), hash(k2));
+ assert_eq!(rand.hash_one(k1), rand.hash_one(k2));
}
}
diff --git a/tests/ui/parse_key.stderr b/tests/ui/parse_key.stderr
index f10c218..15662dc 100644
--- a/tests/ui/parse_key.stderr
+++ b/tests/ui/parse_key.stderr
@@ -2,4 +2,4 @@ error[E0609]: no field `s` on type `&'static str`
--> tests/ui/parse_key.rs:4:16
|
4 | json!({ "".s : true });
- | ^
+ | ^ unknown field