Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Case-insensitive attribute for serde_derive #586

Closed
alexreg opened this issue Oct 14, 2016 · 10 comments
Closed

Case-insensitive attribute for serde_derive #586

alexreg opened this issue Oct 14, 2016 · 10 comments

Comments

@alexreg
Copy link

alexreg commented Oct 14, 2016

Could we have a case-insitive option for the serde attribute on types and fields?

e.g. Something like:

#[derive(Serialize, Deserialize, Debug)]
#[serde(case_insensitive)] // would allow "Foo", "foo", "FOO", etc.
pub struct Foo {
    #[serde(case_insensitive)] // would allow "a" or "A"
    pub a: u32,
    pub b: i16,
    #[serde(case_insensitive, rename = "foo")] // would allow "foo", "FOO", "fOo", etc.
    pub c: String,
}
@dtolnay
Copy link
Member

dtolnay commented Apr 6, 2017

A serde(case_insensitive) attribute would be great!

Currently the generated code for a struct like Foo contains:

fn visit_str<E>(self, field: &str) -> Result<Field, E>
    where E: de::Error
{
    match field {
        "a" => Ok(Field::field0),
        "b" => Ok(Field::field1),
        "foo" => Ok(Field::field2),
        _ => Ok(Field::ignore),
    }
}

The attribute would need to change this to do case insensitive comparison. The only case insensitive comparison in Rust I can find right now is std::ascii::AsciiExt::eq_ignore_ascii_case. Support for unicode-aware comparison is tracked in rust-lang/rfcs#791.

@dtolnay
Copy link
Member

dtolnay commented Apr 6, 2017

Here is one workaround for now - a deserialize_with function that converts to lowercase before deserializing.

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

use serde::de::{self, Deserialize, DeserializeOwned, Deserializer};
use serde_json::Value;

use std::collections::BTreeMap as Map;

#[derive(Deserialize, Debug)]
struct Data {
    aaa: u8,
    bbb: u8,
}

#[derive(Deserialize, Debug)]
struct Wrl {
    #[serde(deserialize_with = "case_insensitive")]
    data: Data,
}

fn case_insensitive<'de, T, D>(deserializer: D) -> Result<T, D::Error>
    where T: DeserializeOwned,
          D: Deserializer<'de>
{
    let map = Map::<String, Value>::deserialize(deserializer)?;
    let lower = map.into_iter().map(|(k, v)| (k.to_lowercase(), v)).collect();
    T::deserialize(Value::Object(lower)).map_err(de::Error::custom)
}

fn main() {
    let j = r#" {"data": {"aaA":1, "BBB":2}} "#;

    println!("{:?}", serde_json::from_str::<Wrl>(j).unwrap());
}

@iddm
Copy link

iddm commented Jun 1, 2017

The serde is 1.0 already but it does not have such a needed feature. Will it ever has it?

@oli-obk
Copy link
Member

oli-obk commented Jun 1, 2017

@vityafx the 1.0 version means that the existing features are well tested and won't be broken in the future. Backwardscompatible changes will be done when we get to them. We'll happily accept PRs addressing this issue. Feel free to open half finished PRs if you need help

@nox
Copy link
Contributor

nox commented Nov 27, 2017

This would also be useful in cbindgen (the mentioned FromStr impls are just there for case insensitivity).

@andoriyu
Copy link

andoriyu commented Mar 1, 2018

Any news on this?

@iddm
Copy link

iddm commented Mar 2, 2018

@andoriyu if it really can be done must with deserializer, it is a good candidate for doing it in serde-aux.

@iddm
Copy link

iddm commented Mar 2, 2018

@andoriyu Added the provided macro_rules! macro to the serde_aux library.

@dtolnay
Copy link
Member

dtolnay commented Jan 6, 2019

Closing as case insensitive functionality is now provided by the serde-aux crate.

@dtolnay dtolnay closed this as completed Jan 6, 2019
@iddm
Copy link

iddm commented Sep 18, 2020

I am now a bit struggling with refactoring. I want to enable case-insensitive parsing for both, enums and structs. I know how to patch serde to do this (there are at least two options for that), but without it, I don't think it is possible to provide a solution solely based on serde's public interface. The solution proposed by @dtolnay uses serde_json knowledge and structures, while in serde we don't have anything like that. It also seems that we don't have a basic field visitor of a struct where you may navigate it to ignore case.

Is there a way to implement the Deserialize trait so that we can manipulate fields' names?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

6 participants