crystal icon indicating copy to clipboard operation
crystal copied to clipboard

`root` property of `JSON::Field` should support nested keys

Open straight-shoota opened this issue 1 year ago • 0 comments

The JSON::Field annotation for JSON::Serializable has a root property to specify a nested key which the parser unwraps.

require "json"
 
class Foo
  include JSON::Serializable
  
  @[JSON::Field(root: "value")]
  property foo : String
end
 
Foo.from_json %({"foo": {"value": "bar"}}) # => #<Foo:0x7fe0894f1e60 @foo="bar">

This is limited to a single nesting layer though. If you want to unwrap more layers, you need to write a custom converter. Sometimes the relevant information is nested in a deeper structure. It would be nice if root could handle that as well. It would be similar to JSON::Any#dig.

An example use case is mentioned in https://forum.crystal-lang.org/t/complex-json-deserialization/6095

It should be a simple enhancement to allow root to be a list of keys describing a path to unwrap.

Example usage:

require "json"
 
class Foo
  include JSON::Serializable
  
  @[JSON::Field(root: ["deeply", "nested", "value"])]
  property foo : String
end
 
Foo.from_json %({"foo": {"deeply": {"nested": {"value": "bar"}}}}) # => #<Foo:0x7fe0894f1e60 @foo="bar">

The same should apply to the root parameter of Object.from_json.

The heavy lifting should probably happen in JSON::PullParser#on_key! (which would also expose this functionality in this lower level API).

straight-shoota avatar Oct 17 '23 09:10 straight-shoota