(a) How do I to do ‘from_json’ on a class that has a property of type Symbol?
e.g.:
require "json"
class Foo
include JSON::Serializable
property state : Symbol
def initialize(@state)
end
end
f = Foo.new(state: :bar)
p! f # => #<Foo:0x7fbff4141eb0 @state=:bar>
p! f.to_json # => "{\"state\":\"bar\"}"
p! Foo.from_json(f.to_json) # => causes "Error: undefined method 'new' for Symbol.class"
(b) Should a Symbol loose or keep the : when converted to JSON? (I’m not sure myself, so I figured I’d ask.)
e.g.: The f.to_json produces "{\"state\":\"bar\"}", but should it produce "{\"state\":\":bar\"}"?
You can’t dynamically create a Symbol, so there’s not really a way to do this. Have you considered using an Enum? E.g. something like:
enum State
FOO
BAR
end
class Foo
include JSON::Serializable
property state : State
def initialize(@state : State)
end
end
f = Foo.new(state: :bar)
p! f # => #<Foo:0x7f124d85eeb0 @state=BAR>
p! f.to_json # => "{\"state\":\"bar\"}"
p! Foo.from_json(f.to_json) # => #<Foo:0x7f124d85ee90 @state=BAR>
It should definitely lose it. JSON doesn’t have the concept of a Symbol. The closest representation is String, hence why it loses the :.
I was wanting to be able to:
(a) import and export a JSON version of the class objects
(b) use an array of Symbols in a class, which the app could add to or remove from this array
So a hard-coded Enum won’t work. I could use Strings instead of Symbols; not as efficient, but probably the best way to be able to do an array of items and still be able to import/export JSON.
Hum; I could ‘recommend’ that the end-user uses Constants for the states/strings; that will help optimize a bit.
What do these symbols represent? If the symbol values are known ahead of time, but there are multiple of them, sounds like a good usecase for Enums - Crystal.
require "json"
class Foo(T)
include JSON::Serializable
property states_allowed : Array(T) = T.values
property state : T
def initialize(@state : T)
end
end
f = Foo(Color).new(state: Color::Red)
p! Foo(Color).from_json(f.to_json)
Do you really need the array of allowed states tho?
I was previously validating that the state requested was in the list of states_allowed. But, due to the nature of Enum’s and the fact that I can get the values, I probably won’t need to specifically note some sort of states_allowed! Plus, the Enum’s methods of .from_value?(value : Int) : self? and .parse?(string : String) : self? will come in very handy!
Now, if I could just get a Proc to be to/from-JSON-compatible!
FWIW this isn’t how you implement JSON::Serializable. The main benefit of JSON::Serializable is that it automatically defines how to (de)serialize the type based on the ivars within it. If all you want to do is manually define a way to serialize the type, just define a def to_json(builder : JSON::Builder) : Nil method and call it a day.
Hit the same issue. Have a property of Hash(Symbol, String) that fails with the exception below during SomeClass.from_json. I’m ignoring the field with @[JSON::Field(ignore: true)] at the moment.
I actually thought this was a bug until I read above that Symbols can’t be created at runtime in which case I think it would be nice to add this to the documentation (JSON::Serializable - Crystal 1.0.0) and/or the compiler error message rather than say undefined method along with a recommended solution.
In /usr/local/Cellar/crystal/1.0.0_1/src/json/from_json.cr:189:20
189 | parsed_key = K.from_json_object_key?(key)
^--------------------
Error: undefined method 'from_json_object_key?' for Symbol.class