serde-json-core

repository·master·Indexed 42 days ago

https://github.com/rust-embedded-community/serde-json-core

A no_std compatible implementation of serde-json providing JSON serialization and deserialization for embedded systems and bare-metal Rust programs. It features zero-allocation operations, efficient integer parsing, and support for heapless collections. Version 0.6.0 requires stable Rust 1.87.0 and up.

Tokens
3.8K
Snippets
14
Records
21
Agent score
51%

What's inside serde-json-core

  1. Understand JSON serialization of floating-point numbers

    master

    When serializing f32 or f64 values:

    • Finite numbers are serialized using the ryu algorithm for high-precision, compact decimal representation.
    • Non-finite numbers (NaN, Infinity, -Infinity) are serialized as null in the resulting JSON.
    // f32::NAN -> null
    // f32::INFINITY -> null
    // 1.23 -> "1.23"
  2. Core features and limitations of serde-json-core

    master

    Key Features

    • Zero Allocation: (De)serialization does not require dynamic memory allocation.
    • Efficient Integer Parsing: Integers are parsed directly into the target type, avoiding u64 intermediate steps to save compiler intrinsics on non-64-bit architectures.
    • Minimal Overhead: Uses a simple C-like enum for error types to reduce memory footprint.
    • Supported Types:
      • bool, Integers, Floats
      • str (zero-copy when not de-escaping)
      • Option, Arrays, Tuples, Structs, C-like enums

    Limitations

    • No Dynamic Values: This crate does not support the dynamic Value type found in standard serde_json because it requires dynamic memory allocation.
    • Compact Format Only: Serialization is limited to the compact JSON format.
  3. JSON string escaping behavior

    master

    The serializer follows standard JSON escaping rules for strings:

    • " and \ are escaped as \" and \\.
    • Control characters like backspace (\b), tab (\t), newline (\n), form feed (\f), and carriage return (\r) are escaped.
    • Control characters in the range U+0000 through U+001F are escaped using the six-character \u00XX format (uppercase hex).
  4. Handle deserialization errors in serde-json-core

    master

    When using serde-json-core::from_str, deserialization can fail due to various reasons. The library provides specific error variants in crate::de::Error to help identify the cause of failure.

    Common error scenarios include:

    • TrailingCharacters: The JSON string contains more data after the valid JSON object has been parsed (e.g., [10, 20, 30] when expecting a tuple of 2).
    • CustomError: A general error returned by the deserializer (e.g., when a tuple struct has the wrong number of elements).
    • ExpectedSomeValue: The JSON structure is incomplete or malformed (e.g., "broken": [ }).

    If you enable the custom-error-messages feature, CustomError is replaced by CustomErrorWithMessage, which provides a descriptive string explaining the failure (e.g., "invalid length 1, expected tuple struct Xy with 2 elements"). Note that these messages may be truncated to preserve memory efficiency in embedded environments.

    // Example of handling a trailing characters error
    let result = crate::from_str::<Xy>(r#"[10, 20, 30]"#);
    assert_eq!(result, Err(crate::de::Error::TrailingCharacters));
    
    // Example of a custom error with the 'custom-error-messages' feature
    // #[cfg(feature = "custom-error-messages")]
    // assert_eq!(result, Err(crate::de::Error::CustomErrorWithMessage("...".parse().unwrap())));
  5. JSON Map Key constraints and errors

    master

    When deserializing JSON objects (maps), serde-json-core enforces specific rules for keys:

    • Keys must be strings: If a key is not a string (e.g., a number or boolean), the deserializer returns Error::KeyMustBeAString.
    • Trailing commas are not supported: If a comma is found immediately before the closing brace }, the deserializer returns Error::TrailingComma.
    • Missing separators: If the expected comma or object end is not found between entries, it may return Error::ExpectedObjectCommaOrEnd.
    • Unexpected EOF: If the input ends while parsing an object, it returns Error::EofWhileParsingObject or Error::EofWhileParsingValue.

    These errors are surfaced through the MapAccess implementation during the next_key_seed phase.

  6. Handle `BufferFull` serialization errors

    master

    The primary error encountered during serialization is Error::BufferFull. This occurs when the target buffer (either a slice or a heapless collection) is too small to hold the resulting JSON string. Ensure your buffers are sized appropriately for your data structures.

    use serde_json_core::Error;
    
    match serde_json_core::to_slice(&large_data, &mut small_buf) {
        Ok(len) => println!("Serialized {} bytes", len),
        Err(Error::BufferFull) => println!("Buffer was too small!"),
        Err(e) => println!("Other error: {:?}", e),
    }
  7. Serialize data to a `heapless` String or Vec

    master

    If the heapless feature is enabled, you can use to_string and to_vec to serialize data into fixed-capacity heapless collections. This is useful in no_std environments where you want to avoid manual buffer management but still require deterministic memory usage.

    // Requires 'heapless' feature
    use heapless::String;
    
    // Serialize to a fixed-capacity String
    let s: String<128> = serde_json_core::to_string(&my_struct)?;
    
    // Serialize to a fixed-capacity Vec
    use heapless::Vec;
    let v: Vec<u8, 128> = serde_json_core::to_vec(&my_struct)?;
  8. Use EscapedStr to borrow escaped JSON strings

    master

    When deserializing JSON that contains escape sequences (like \n, \uXXXX, or \"), standard string deserialization usually requires allocating a new string to hold the unescaped result. To avoid allocation in embedded environments, you can use serde_json_core::str::EscapedStr<'a>.

    EscapedStr allows you to borrow the raw, escaped string directly from the input buffer. You can then iterate over its fragments to process the unescaped characters one by one.

    #[derive(serde::Deserialize)]
    struct Event<'a> {
        name: heapless::String<16>,
        #[serde(borrow)]
        description: serde_json_core::str::EscapedStr<'a>,
    }
    
    // Use from_str_escaped or from_slice_escaped to populate EscapedStr
    serde_json_core::de::from_str_escaped::<Event<'_>>(
        r#"{ "name": "Party\u0021", "description": "I'm throwing a party! Hopefully the \u2600 shines!" }"#,
        &mut [0; 8],
    ).unwrap();
  9. Deserialize JSON from a byte slice with unescaping support

    master

    Use from_slice_escaped to deserialize JSON bytes while providing a mutable buffer for unescaping strings. This is necessary when deserializing into owned string types that cannot simply borrow from the input slice.

    let mut buffer = [0u8; 64];
    let (value, bytes_consumed) = serde_json_core::from_slice_escaped::<MyStruct>(json_bytes, &mut buffer)?;
  10. Serialize Rust types to slices

    master

    Use serde_json_core::to_slice to serialize a Rust data structure into a provided mutable byte slice. This is a non-allocating operation suitable for resource-constrained devices.

    use serde::{Serialize, Deserialize};
    
    #[derive(Serialize, Deserialize, Debug, PartialEq)]
    struct Data<'a> {
        value: u32,
        message: &'a str,
    }
    
    let data = Data { value: 10, message: "Hello, World!" };
    let mut buffer = [0u8; 256];
    
    // Serializing into the buffer
    let len = serde_json_core::to_slice(&data, &mut buffer[..]).unwrap();
    
    // The valid JSON is in buffer[..len]