jsonapi.rb icon indicating copy to clipboard operation
jsonapi.rb copied to clipboard

Case-aware deserialization support

Open ballPointPenguin opened this issue 6 years ago • 5 comments

json-api clients adhering to the 1.0 recommendations may be likely to use dasherized (e.g. password-confirmation) keys. json-api clients adhering to the 1.1 recommendations may be likely to use camelCase (e.g. passwordConfirmation) keys. The JSONAPI::Deserialization jsonapi_deserialize method does not seem to offer modification to key names. It would be handy if it could (optionally) underscore-ify (snake-case) these keys, (e.g. password_confirmation) to conform to Rails expectations.

ballPointPenguin avatar Oct 17 '19 04:10 ballPointPenguin

@ballPointPenguin I could take a look, though I have little to no use-cases related to this. So if you or somebody else has more free time, I'll be happy to review and merge a PR.

Thanks for the request!

stas avatar Oct 17 '19 11:10 stas

Yes I'm curious if anybody else cares about this. In my case, I can easily modify my web client to use under_score case when serializing attributes to send to the API. But I would want to expect that any backend implementation of the json-api spec would conform to either the 1.0 kebab-case or the 1.1 camelCase recommendation. If I get inspired I may make a PR for this.

ballPointPenguin avatar Oct 21 '19 14:10 ballPointPenguin

So far we've had to add a few monkey patches to get dash-case into snake_case and how we have some js clients wanting to use 1.1 with camelCase. It would be good to take in includes and params that are converted to snake_case automatically.

We sprinkle in deep_transform_keys{ |key| key.to_s.underscore }.to_s.split(',').map(&:strip).compact

2.6.5 :011 > { "foo-bar": "cat" }.deep_transform_keys{ |key| key.to_s.underscore }.to_s.split(',').map(&:strip).compact => ["{"foo_bar"=>"cat"}"] 2.6.5 :012 > { "fooBar": "cat" }.deep_transform_keys{ |key| key.to_s.underscore }.to_s.split(',').map(&:strip).compact => ["{"foo_bar"=>"cat"}"]

The serialiser already supports spitting things out in the format we expect and you can pass in a param so it knows to return camelCase or dash-case. Rails itself will forever be snake_case so we don't need to worry about that.

code-bunny avatar Mar 04 '20 22:03 code-bunny

For rails 5 plus I found this little snippet that you can add to an initialiser instead of monkey patching the gem. The only issue comes where we are wanting to use the includes and filter keywords with non rails case.

# Transform JSON request param keys from JSON-conventional camelCase to
# Rails-conventional snake_case:
ActionDispatch::Request.parameter_parsers[:json] = -> (raw_post) {
  # Modified from action_dispatch/http/parameters.rb
  data = ActiveSupport::JSON.decode(raw_post)
  data = {:_json => data} unless data.is_a?(Hash)

  # Transform camelCase param keys to snake_case:
  data.deep_transform_keys!(&:underscore)
}

code-bunny avatar Mar 16 '20 16:03 code-bunny

I'd like to make it clear that the above can have unexpected effect on the serialized document. See the recent discussion regarding this exact issue: https://github.com/fast-jsonapi/fast_jsonapi/issues/62#issuecomment-590415588

If you still want to hook into the pre-deserialization, consider the parsers API in Rails, we leverage that and right now it's just aliasing the json parser: https://github.com/stas/jsonapi.rb/blob/master/lib/jsonapi/rails.rb#L24-L28

Or alternatively, use the deserialization support we have and overwrite the (application) controller method.

:warning::warning::warning: Again, JSONAPI format doesn't apply the type transformation over the whole document, just to the spec-covered parts of it, so be careful with what you roll out into production.

stas avatar Mar 16 '20 16:03 stas