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

Support more Literal types in JSON::Serializable's use_json_discriminator macro #9222

Merged
merged 5 commits into from
Jan 11, 2021

Conversation

voximity
Copy link
Contributor

@voximity voximity commented May 3, 2020

This PR supports the following Literal types as keys for hashes passed to use_json_discriminator:

  • StringLiteral
  • NumberLiteral
  • BoolLiteral
  • Path

This allows more types to be expected from a discriminator value. Previously, use_json_discriminator was hard-coded to expect a string value. String, boolean, and integer values are now supported. An example:

require "json"

enum Shapes
  Point
  Circle
end

class Shape
  include JSON::Serializable

  use_json_discriminator "type", {Shapes::Point => Point, Shapes::Circle => Circle}
end

class Point < Shape
end

class Point < Circle
end

Shape.from_json %({"type": 0}) # a Point
Shape.from_json %({"type": 1}) # a Circle

@asterite
Copy link
Member

asterite commented May 3, 2020

Thank you!

Do you have an example API in mind where this can be useful?

@voximity
Copy link
Contributor Author

voximity commented May 3, 2020

@asterite My specific use case is discriminating Channel objects from Discord's API into their respective types. Discord uses integer enums explicitly to describe object types. I tried implementing an enum similar to the example I give in the original comment, but the old implementation expects a string over an integer for the field value.

Implementing Path and NumberLiteral fixes this, but I also added BoolLiteral as it's easy to support, and perhaps one day there'll be some API that decides discriminating object types by a boolean field is necessary!

@asterite
Copy link
Member

asterite commented May 3, 2020

Strange, I think enum should support deserialization from int and string, instead of this PR.

@voximity
Copy link
Contributor Author

voximity commented May 3, 2020

The main issue is that previously discriminator_value was always fetched by pull.read_string, but the value is not always a string.

@asterite
Copy link
Member

asterite commented May 3, 2020

Oooooh... got it! Sorry, I should have looked at the link you sent, I was on mobile.

Yeah, it makes perfect sense then. 🙏

when {{key.id.stringify}}
{% else %}
{% if key.is_a?(StringLiteral) %}
when {{key.id.stringify}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think key.id.stringify is just key?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you're right! I just kept it this way because I was looking at the NamedTupleLiteral stringification.

@caspiano
Copy link
Contributor

caspiano commented Nov 2, 2020

pardon my asking @bcardiff, but is this going in 1.0.0?

@Blacksmoke16
Copy link
Member

Probably should do this for YAML too no?

@voximity
Copy link
Contributor Author

voximity commented Nov 2, 2020

Probably. Didn't think of it/need it when I opened this PR. I may get around to it in some time.

Copy link
Member

@bcardiff bcardiff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, this felt out of my radar so far.
Looks good. I have an optional comment for completeness.

It needs a rebase, but I am ok to include for 1.0

@@ -393,7 +393,7 @@ module JSON
def self.new(pull : ::JSON::PullParser)
location = pull.location

discriminator_value = nil
discriminator_value : String | Int64 | Bool | Nil = nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we don't need Float64 here, but it seems the atomic values of JSON::Any are wanted here, except for Float64

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable really doesn't need any type restrictions at all.

@straight-shoota straight-shoota added this to the 1.0.0 milestone Dec 30, 2020
@bcardiff bcardiff merged commit 0694ea0 into crystal-lang:master Jan 11, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants