serde_hash
A Rust library for seamlessly integrating HashIds with Serde serialization and deserialization. This library provides a convenient way to obfuscate numeric IDs in your JSON output without changing your application's internal data structures.
Features
- Extends serde's
SerializeandDeserializederives -- all serde attributes (rename,alias,default,skip, etc.) work alongside hash encoding - Automatically convert numeric IDs to hash strings during serialization
- Transparently decode hash strings back to numeric IDs during deserialization
- Configurable hash generation with customizable salt, minimum length, and character set
- Simple attribute-based field marking with
#[serde(hash)] - Secure random salt generation
Installation
Add serde_hash to your Cargo.toml:
[]
= "0.2"
= { = "1.0", = ["derive"] }
= "1.0" # If using JSON serialization
Supported Types
The hash attribute is only compatible with unsigned integer types and collections of them. It cannot be used with floating-point (f32, f64) or signed integer (i32, i64) types.
| Category | Supported Types |
|---|---|
| Unsigned integers | u8, u16, u32, u64, u128, usize |
| Optional unsigned integers | Option<u8>, Option<u16>, ..., Option<usize> |
| Vectors of unsigned integers | Vec<u8>, Vec<u16>, ..., Vec<usize> |
| Optional vectors | Option<Vec<u8>>, Option<Vec<u16>>, ..., Option<Vec<usize>> |
Usage
Configuration Options
Customize hash settings with SerdeHashOptions. Call .build() once at startup before any serialization.
| Name | Default Value | Description |
|---|---|---|
| salt | Generated randomly | The cryptographic salt used for hash generation |
| min_length | 8 | Minimum length of the generated hash string |
| alphabet | Alphanumeric (a-zA-Z0-9) | Characters used for hash encoding |
Simplest example:
use SerdeHashOptions;
new.build;
A more complete example:
use SerdeHashOptions;
new
.with_salt
.with_min_length
.with_alphabet
.build;
Basic Example
Place #[serde_hash] above your derive and mark fields with #[serde(hash)]. All standard serde attributes work alongside hash:
use ;
use serde_hash;
use SerdeHashOptions;
Notice how #[serde(hash, alias = "identifier")] works -- hash triggers hash encoding while alias is handled by serde normally. The rename on name also works as expected.
Deserialization Example
Hash strings are transparently decoded back to numeric IDs:
use ;
use serde_hash;
use SerdeHashOptions;
Using with Vectors and Options
#[serde(hash)] works with Vec<T>, Option<T>, and Option<Vec<T>> where T is an unsigned integer:
use ;
use serde_hash;
use SerdeHashOptions;
Generating Secure Salt
For production use, generate a cryptographically secure random salt:
use ;
let salt = generate_salt;
new
.with_salt
.with_min_length
.build;
How It Works
The #[serde_hash] attribute macro runs before serde's derive macros. It transforms #[serde(hash)] into serde's #[serde(with = "...")] attribute, pointing to built-in serialize/deserialize functions that handle hash encoding. This means:
- Serde's own derive generates the
Serialize/Deserializeimplementations - All serde attributes (
rename,alias,default,skip,flatten, etc.) work normally - Hash encoding is applied only to the marked fields via serde's
withmechanism
Why Use serde_hash?
- Obfuscation: Hide your internal database IDs from API consumers
- Serde compatible: Works with all serde attributes and any serde-based format (JSON, TOML, MessagePack, etc.)
- Predictability: Unlike UUID generation, the same ID always hashes to the same string with the same settings
- Transparency: Your application code works with numeric IDs while your API exposes hash strings
- Simplicity: Add one attribute macro and configure once
License
This project is built with Rust and uses the hash-ids crate for hash ID generation.