
Line-by-line transliteration of serde_with offering custom de/serialization helpers, serde_as flexibility, large-array/bytes handling, DisplayFromStr conversions, skip_serializing_none, prefix/suffix macros, comma-separated parsing, const-generic array support and specialized Bytes encoding.
This is a Kotlin Multiplatform line-by-line transliteration port of jonasbb/serde_with.
Original Project: This port is based on jonasbb/serde_with. All design credit and project intent belong to the upstream authors; this repository is a faithful port to Kotlin Multiplatform with no behavioural changes intended.
This is an in-progress port. The goal is feature parity with the upstream Rust crate while providing a native Kotlin Multiplatform API. Every Kotlin file carries a // port-lint: source <path> header naming its upstream Rust counterpart so the AST-distance tool can track provenance.
The text below is reproduced and lightly edited from
https://github.com/jonasbb/serde_with/. It is the upstream project's own description and remains under the upstream authors' authorship; links have been rewritten to absolute upstream URLs so they continue to resolve from this repository.
This crate provides custom de/serialization helpers to use in combination with serde's with annotation and with the improved serde_as-annotation.
Some common use cases are:
Display and FromStr traits, e.g., for u8, url::Url, or mime::Mime.
Check DisplayFromStr for details.serde_as large arrays are supported, even if they are nested in other types.
[bool; 64], Option<[u8; M]>, and Box<[[u8; 64]; N]> are all supported, as this examples shows.Option types with #[skip_serializing_none].with_prefix! / with_suffix!.#hash,#tags,#are,#great into a Vec<String>.
Check the documentation for serde_with::StringWithSeparator::<CommaSeparator, T>.Check out the user guide to find out more tips and tricks about this crate.
For further help using this crate, you can open a new discussion or ask on users.rust-lang.org. For bugs, please open a new issue on GitHub.
# Add the current version to your Cargo.toml
cargo add serde_withThe crate contains different features for integration with other common crates. Check the feature flags section for information about all available features.
Annotate your struct or enum to enable the custom de/serializer.
The #[serde_as] attribute must be placed before the #[derive].
The as is analogous to the with attribute of serde.
You mirror the type structure of the field you want to de/serialize.
You can specify converters for the inner types of a field, e.g., Vec<DisplayFromStr>.
The default de/serialization behavior can be restored by using _ as a placeholder, e.g., BTreeMap<_, DisplayFromStr>.
#[serde_as]
#[derive(Deserialize, Serialize)]
struct Foo {
// Serialize with Display, deserialize with FromStr
#[serde_as(as = "DisplayFromStr")]
bar: u8,
}
// This will serialize
Foo {bar: 12}
// into this JSON
{"bar": "12"}serde does not support arrays with more than 32 elements or using const-generics.
The serde_as attribute allows circumventing this restriction, even for nested types and nested arrays.
On top of it, [u8; N] (aka, bytes) can use the specialized "Bytes" for efficiency much like the serde_bytes crate.
#[serde_as]
#[derive(Deserialize, Serialize)]
struct Arrays<const N: usize, const M: usize> {
#[serde_as(as = "[_; N]")]
constgeneric: [bool; N],
#[serde_as(as = "Box<[[_; 64]; N]>")]
nested: Box<[[u8; 64]; N]>,
#[serde_as(as = "Option<[_; M]>")]
optional: Option<[u8; M]>,
#[serde_as(as = "Bytes")]
bytes: [u8; M],
}
// This allows us to serialize a struct like this
let arrays: Arrays<100, 128> = Arrays {
constgeneric: [true; 100],
nested: Box::new([[111; 64]; 100]),
optional: Some([222; 128]),
bytes: [0x42; 128],
};
assert!(serde_json::to_string(&arrays).is_ok());This situation often occurs with JSON, but other formats also support optional fields.
If many fields are optional, putting the annotations on the structs can become tedious.
The #[skip_serializing_none] attribute must be placed before the #[derive].
#[skip_serializing_none]
#[derive(Deserialize, Serialize)]
struct Foo {
a: Option<usize>,
b: Option<usize>,
c: Option<usize>,
d: Option<usize>,
e: Option<usize>,
f: Option<usize>,
g: Option<usize>,
}
// This will serialize
Foo {a: None, b: None, c: None, d: Some(4), e: None, f: None, g: Some(7)}
// into this JSON
{"d": 4, "g": 7}This example is mainly supposed to highlight the flexibility of the serde_as annotation compared to serde's with annotation.
More details about serde_as can be found in the user guide.
use std::time::Duration;
#[serde_as]
#[derive(Deserialize, Serialize)]
enum Foo {
Durations(
// Serialize them into a list of number as seconds
#[serde_as(as = "Vec<DurationSeconds>")]
Vec<Duration>,
),
Bytes {
// We can treat a Vec like a map with duplicates.
// JSON only allows string keys, so convert i32 to strings
// The bytes will be hex encoded
#[serde_as(as = "Map<DisplayFromStr, Hex>")]
bytes: Vec<(i32, Vec<u8>)>,
}
}
// This will serialize
Foo::Durations(
vec![Duration::new(5, 0), Duration::new(3600, 0), Duration::new(0, 0)]
)
// into this JSON
{
"Durations": [5, 3600, 0]
}
// and serializes
Foo::Bytes {
bytes: vec![
(1, vec![0, 1, 2]),
(-100, vec![100, 200, 255]),
(1, vec![0, 111, 222]),
],
}
// into this JSON
{
"Bytes": {
"bytes": {
"1": "000102",
"-100": "64c8ff",
"1": "006fde"
}
}
}Licensed under either of
at your option.
For detailed contribution instructions please read CONTRIBUTING.md.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.
dependencies {
implementation("io.github.kotlinmania:serde-with-kotlin:0.1.0")
}./gradlew build
./gradlew testSee AGENTS.md and CLAUDE.md for translator discipline, port-lint header convention, and Rust → Kotlin idiom mapping.
This Kotlin port is distributed under the same MIT license as the upstream jonasbb/serde_with. See LICENSE (and any sibling LICENSE-* / NOTICE files mirrored from upstream) for the full text.
Original work copyrighted by the serde_with authors.
Kotlin port: Copyright (c) 2026 Sydney Renee and The Solace Project.
Thanks to the jonasbb/serde_with maintainers and contributors for the original Rust implementation. This port reproduces their work in Kotlin Multiplatform; bug reports about upstream design or behavior should go to the upstream repository.
This is a Kotlin Multiplatform line-by-line transliteration port of jonasbb/serde_with.
Original Project: This port is based on jonasbb/serde_with. All design credit and project intent belong to the upstream authors; this repository is a faithful port to Kotlin Multiplatform with no behavioural changes intended.
This is an in-progress port. The goal is feature parity with the upstream Rust crate while providing a native Kotlin Multiplatform API. Every Kotlin file carries a // port-lint: source <path> header naming its upstream Rust counterpart so the AST-distance tool can track provenance.
The text below is reproduced and lightly edited from
https://github.com/jonasbb/serde_with/. It is the upstream project's own description and remains under the upstream authors' authorship; links have been rewritten to absolute upstream URLs so they continue to resolve from this repository.
This crate provides custom de/serialization helpers to use in combination with serde's with annotation and with the improved serde_as-annotation.
Some common use cases are:
Display and FromStr traits, e.g., for u8, url::Url, or mime::Mime.
Check DisplayFromStr for details.serde_as large arrays are supported, even if they are nested in other types.
[bool; 64], Option<[u8; M]>, and Box<[[u8; 64]; N]> are all supported, as this examples shows.Option types with #[skip_serializing_none].with_prefix! / with_suffix!.#hash,#tags,#are,#great into a Vec<String>.
Check the documentation for serde_with::StringWithSeparator::<CommaSeparator, T>.Check out the user guide to find out more tips and tricks about this crate.
For further help using this crate, you can open a new discussion or ask on users.rust-lang.org. For bugs, please open a new issue on GitHub.
# Add the current version to your Cargo.toml
cargo add serde_withThe crate contains different features for integration with other common crates. Check the feature flags section for information about all available features.
Annotate your struct or enum to enable the custom de/serializer.
The #[serde_as] attribute must be placed before the #[derive].
The as is analogous to the with attribute of serde.
You mirror the type structure of the field you want to de/serialize.
You can specify converters for the inner types of a field, e.g., Vec<DisplayFromStr>.
The default de/serialization behavior can be restored by using _ as a placeholder, e.g., BTreeMap<_, DisplayFromStr>.
#[serde_as]
#[derive(Deserialize, Serialize)]
struct Foo {
// Serialize with Display, deserialize with FromStr
#[serde_as(as = "DisplayFromStr")]
bar: u8,
}
// This will serialize
Foo {bar: 12}
// into this JSON
{"bar": "12"}serde does not support arrays with more than 32 elements or using const-generics.
The serde_as attribute allows circumventing this restriction, even for nested types and nested arrays.
On top of it, [u8; N] (aka, bytes) can use the specialized "Bytes" for efficiency much like the serde_bytes crate.
#[serde_as]
#[derive(Deserialize, Serialize)]
struct Arrays<const N: usize, const M: usize> {
#[serde_as(as = "[_; N]")]
constgeneric: [bool; N],
#[serde_as(as = "Box<[[_; 64]; N]>")]
nested: Box<[[u8; 64]; N]>,
#[serde_as(as = "Option<[_; M]>")]
optional: Option<[u8; M]>,
#[serde_as(as = "Bytes")]
bytes: [u8; M],
}
// This allows us to serialize a struct like this
let arrays: Arrays<100, 128> = Arrays {
constgeneric: [true; 100],
nested: Box::new([[111; 64]; 100]),
optional: Some([222; 128]),
bytes: [0x42; 128],
};
assert!(serde_json::to_string(&arrays).is_ok());This situation often occurs with JSON, but other formats also support optional fields.
If many fields are optional, putting the annotations on the structs can become tedious.
The #[skip_serializing_none] attribute must be placed before the #[derive].
#[skip_serializing_none]
#[derive(Deserialize, Serialize)]
struct Foo {
a: Option<usize>,
b: Option<usize>,
c: Option<usize>,
d: Option<usize>,
e: Option<usize>,
f: Option<usize>,
g: Option<usize>,
}
// This will serialize
Foo {a: None, b: None, c: None, d: Some(4), e: None, f: None, g: Some(7)}
// into this JSON
{"d": 4, "g": 7}This example is mainly supposed to highlight the flexibility of the serde_as annotation compared to serde's with annotation.
More details about serde_as can be found in the user guide.
use std::time::Duration;
#[serde_as]
#[derive(Deserialize, Serialize)]
enum Foo {
Durations(
// Serialize them into a list of number as seconds
#[serde_as(as = "Vec<DurationSeconds>")]
Vec<Duration>,
),
Bytes {
// We can treat a Vec like a map with duplicates.
// JSON only allows string keys, so convert i32 to strings
// The bytes will be hex encoded
#[serde_as(as = "Map<DisplayFromStr, Hex>")]
bytes: Vec<(i32, Vec<u8>)>,
}
}
// This will serialize
Foo::Durations(
vec![Duration::new(5, 0), Duration::new(3600, 0), Duration::new(0, 0)]
)
// into this JSON
{
"Durations": [5, 3600, 0]
}
// and serializes
Foo::Bytes {
bytes: vec![
(1, vec![0, 1, 2]),
(-100, vec![100, 200, 255]),
(1, vec![0, 111, 222]),
],
}
// into this JSON
{
"Bytes": {
"bytes": {
"1": "000102",
"-100": "64c8ff",
"1": "006fde"
}
}
}Licensed under either of
at your option.
For detailed contribution instructions please read CONTRIBUTING.md.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.
dependencies {
implementation("io.github.kotlinmania:serde-with-kotlin:0.1.0")
}./gradlew build
./gradlew testSee AGENTS.md and CLAUDE.md for translator discipline, port-lint header convention, and Rust → Kotlin idiom mapping.
This Kotlin port is distributed under the same MIT license as the upstream jonasbb/serde_with. See LICENSE (and any sibling LICENSE-* / NOTICE files mirrored from upstream) for the full text.
Original work copyrighted by the serde_with authors.
Kotlin port: Copyright (c) 2026 Sydney Renee and The Solace Project.
Thanks to the jonasbb/serde_with maintainers and contributors for the original Rust implementation. This port reproduces their work in Kotlin Multiplatform; bug reports about upstream design or behavior should go to the upstream repository.