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

How to deserialize a checkbox #68

Open
ssendev opened this issue Aug 14, 2022 · 4 comments
Open

How to deserialize a checkbox #68

ssendev opened this issue Aug 14, 2022 · 4 comments

Comments

@ssendev
Copy link

ssendev commented Aug 14, 2022

In Ruby on Rails checkboxes are usually used like

<input type="hidden" name="box" value="false" />
<input type="checkbox" name="box" value="true" />

since that way it's possible to model an Option<bool> but that causes Failed to deserialize query string: duplicate field 'box'
Adding #[serde(keep_last)] is apparently not a thing: serde-rs/serde#690.
Is there a way to get it to work?

@samscott89
Copy link
Owner

samscott89 commented Aug 16, 2022

Hey @ssendev. That's an interesting case! I haven't seen that before. So the query string that's generated would be post?box=false&box=true if the checkbox is checked? That's kind of surprising.

Can you show me where that comes up? For example, checking out the rails guide, that's not what I'm seeing: https://guides.rubyonrails.org/v5.0/form_helpers.html#checkboxes

@samscott89
Copy link
Owner

It wouldn't be possible to handle this in serde_qs currently. It would need to be added as new logic.

If you can change the name fields, you could do:

<input type="hidden" name="box[]" value="false" />
<input type="checkbox" name="box[]" value="true" />

and deserialize to a box: Vec<bool>. Which you could make into a bool with box.iter().any(|checked| checked)

@ssendev
Copy link
Author

ssendev commented Aug 19, 2022

@samscott89 Yes that's the query string. The hidden field is there when instead of check_box_tag check_box is used.

Yeah I used a similar Vec workaround though mine was more janky. I guess i hoped there would be something similar to the Flatten workaround which would allow to have an Option<bool>.

Usability wise I imagined it similar to serde_with. Something like

#[derive(Deserialize, Serialize, Debug, PartialEq)]
struct Query {
    #[serde_into(into="vec_to_bool", over="Vec<bool>")]
    common: Option<bool>,
}

fn vec_to_bool(vec: Vec<bool>) -> Option<bool> {
  if vec.empty() {
    return None
  } else {
    Some(vec.iter().any(|checked| checked)
  }
}

But i can live with the Vec in the struct it just leaks some implementation details. So just consider this a low prio feature request and maybe someone comes along that knows of a solution.

@samscott89
Copy link
Owner

Thank you for the reference, that's exactly what I was looking for.

This line is very telling:

Since the HTML specification says key/value pairs have to be sent in the same order they appear in the form, and parameters extraction gets the last occurrence of any repeated key in the query string, that works for ordinary forms.

Whereas what's implemented here is something stricter: make sure there are no repeated keys (that aren't vector elements as indicated by [] or [i]).

I'll change this behaviour to match the spec, probably with an option somewhere to toggle the previous behaviour. Thank you!

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

No branches or pull requests

2 participants