Skip to content

Commit cf16f75

Browse files
Add RawValue::from_string_unchecked
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 827a315 commit cf16f75

2 files changed

Lines changed: 67 additions & 0 deletions

File tree

src/raw.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,40 @@ impl RawValue {
191191
Ok(Self::from_owned(json.into_boxed_str()))
192192
}
193193

194+
/// Convert an owned `String` of JSON data to an owned `RawValue` without
195+
/// checking that it contains valid JSON.
196+
///
197+
/// This is the unchecked counterpart of [`RawValue::from_string`], for
198+
/// strings that are already known to be valid JSON, such as the output of
199+
/// another JSON serializer. Unlike `from_string`, it does not re-parse the
200+
/// string; the only cost is `String::into_boxed_str`.
201+
///
202+
/// # Safety
203+
///
204+
/// The string passed in must contain a single well-formed JSON value with
205+
/// no leading or trailing whitespace. `RawValue` is written verbatim into
206+
/// JSON output wherever it is embedded, and other code (including unsafe
207+
/// code) is allowed to rely on `RawValue` upholding this invariant, in the
208+
/// same way that unsafe code may rely on `str` containing valid UTF-8.
209+
///
210+
/// # Example
211+
///
212+
/// ```
213+
/// use serde_json::value::RawValue;
214+
///
215+
/// let json = serde_json::to_string(&[1, 2, 3])?;
216+
///
217+
/// // SAFETY: `json` was produced by serde_json's own serializer, so it is
218+
/// // a single well-formed JSON value without surrounding whitespace.
219+
/// let raw = unsafe { RawValue::from_string_unchecked(json) };
220+
///
221+
/// assert_eq!(raw.get(), "[1,2,3]");
222+
/// # Ok::<(), serde_json::Error>(())
223+
/// ```
224+
pub unsafe fn from_string_unchecked(json: String) -> Box<Self> {
225+
Self::from_owned(json.into_boxed_str())
226+
}
227+
194228
/// Access the JSON text underlying a raw value.
195229
///
196230
/// # Example

tests/test.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2455,6 +2455,39 @@ fn test_boxed_raw_value() {
24552455
assert_eq!(r#"["a",42,{"foo": "bar"},null]"#, array_to_string);
24562456
}
24572457

2458+
#[cfg(feature = "raw_value")]
2459+
#[test]
2460+
fn test_raw_value_from_string_unchecked() {
2461+
#[derive(Serialize)]
2462+
struct Wrapper {
2463+
a: i8,
2464+
b: Box<RawValue>,
2465+
c: i8,
2466+
}
2467+
2468+
let json = r#"{"foo":[1,2,3],"bar":"\"escaped\""}"#.to_owned();
2469+
let checked = RawValue::from_string(json.clone()).unwrap();
2470+
let unchecked = unsafe { RawValue::from_string_unchecked(json) };
2471+
assert_eq!(checked.get(), unchecked.get());
2472+
2473+
let wrapper = Wrapper {
2474+
a: 1,
2475+
b: unchecked,
2476+
c: 3,
2477+
};
2478+
let wrapper_to_string = serde_json::to_string(&wrapper).unwrap();
2479+
assert_eq!(
2480+
r#"{"a":1,"b":{"foo":[1,2,3],"bar":"\"escaped\""},"c":3}"#,
2481+
wrapper_to_string,
2482+
);
2483+
2484+
// A string with excess capacity is preserved as-is.
2485+
let mut spare = String::with_capacity(64);
2486+
spare.push_str("[1,2,3]");
2487+
let raw = unsafe { RawValue::from_string_unchecked(spare) };
2488+
assert_eq!("[1,2,3]", raw.get());
2489+
}
2490+
24582491
#[cfg(feature = "raw_value")]
24592492
#[test]
24602493
fn test_raw_invalid_utf8() {

0 commit comments

Comments
 (0)