-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
JSON::Serializable #6082
JSON::Serializable #6082
Conversation
Awesome! Can you put the whole setializable spec inside a compare_versions? Annotations don't parse well right now. I'll take a look at the formatter issue soon, it should be simple to fix. And let's do the same for YAML (it can be in this PR or someone else can send it). I'll add the next version of the compiler to travis so we can run that code inside compare_versions. |
We already run the spec suite once with a compiled compiler @asterite - we just don't have anyway way of detecting that the compiler is compiled. |
@RX14 What I mean is that I just added Does that make sense? I just enabled it, but it still fails because the specs should also be behind a |
@asterite it makes sense but I don't like it :) I'd rather just detect if the version has the git suffix or something. |
What git suffix? Where? |
i also find this strange compilation bug: require "json"
module JSONAttrModule
property moo : Int32 = 10
end
class JSONAttrModuleTest
include JSONAttrModule
include JSON::Serializable
@[JSON::Field(key: "phoo")]
property foo = 15
def initialize; end
end
class JSONAttrModuleTest2 < JSONAttrModuleTest
property bar : Int32
def initialize(@bar : Int32)
end
end
p JSONAttrModuleTest2.new(1)
p JSONAttrModuleTest2.from_json(%<{"bar":1}>)
|
@kostya When you inherit a type and define a new class JSONAttrModuleTest2 < JSONAttrModuleTest
property bar : Int32
def initialize(@bar : Int32)
end
def initialize(pull : JSON::PullParser)
{% @type %} # This is a hack that's needed, not sure if it's needed here, though
super
end
end |
is it bug, or why it cant work without redefine? |
It's similar to this: class Foo
def initialize(@x : Int32)
end
end
class Bar < Foo
# This initialize hides all initializes from superclasses, like in Java and C#,
# because at this point we can't be sure all instance vars from Bar will be
# initialized in Foo's initialize.
def initialize(x, y)
super(x)
end
end
Bar.new(1) So we must add an |
It's not a bug, it's how the language has always worked, I explained it a bit in the last comment. The only "bug" is having to use that |
what do you think about this options: d5b218e |
@kostya You can't use {% if compare_versoins(...) %}
# put all the current code here
{% end %} |
Strange: in src/json.cr:97: Not a semantic version: "0.24.2+?"
{% if compare_versions(Crystal::VERSION, "0.25.0") >= 0 %}
^~~~~~~~~~~~~~~~ What's that "0.24.2+?"... |
@asterite the crystal version code calls out to git to find out how many commits it is ahead of the 0.24.2 tag. The answer here was clearly |
Maybe it's better then to implement this after we release 0.25.0. |
@asterite |
Maybe just |
@asterite no, the version of crystal on the ci isn't 0.25.0 because 0.25.0 hasn't been released yet. We must use |
@RX14 I just added Or am I missing something? |
@asterite There's nothing wrong with that technically, I'm arguing against it semantically (we already have a solution by testing with |
@RX14 Could you write a snippet on how you think we can use what you say? I can't imagine it. That is, please write a small diff of this PR using what you suggest. |
(and of course, that also has to work on our machine... in my machine the version is "0.24.2") |
@asterite compilers that don't support annotations are |
Ah, OK. Sounds good then. |
Oops, I mean |
spec/std/json/serializable_spec.cr
Outdated
@@ -1,4 +1,4 @@ | |||
{% if compare_versions(Crystal::VERSION, "0.25.0") >= 0 %} | |||
{% if Crystal::VERSION.includes?("0.24.2+") %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|| Crystal::VERSION == "0.25.0"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually please use || Crystal::VERSION.includes?("0.25.0")
.
src/json.cr
Outdated
@@ -94,6 +94,6 @@ end | |||
|
|||
require "./json/*" | |||
|
|||
{% if compare_versions(Crystal::VERSION, "0.25.0") >= 0 %} | |||
{% if Crystal::VERSION.includes?("0.24.2+") %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|| Crystal::VERSION == "0.25.0"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
somehow this is locally crashed:
and when remove conditions works. |
Now you are inside the |
my english is bad, i cant write normal docs :) |
@kostya That's a nice idea, but the names could conflict with other method names, like About knowing the options, it can be documented. We can improve all of this later, if needed. |
@kostya That's a nice idea, but |
@RX14 I wouldn't mind shipping this without documentation. I guess no one except kostya tried this PR, so all of this can be experimental for now. If all works well, we can document it for 0.25.1 and also fix bugs that might appear, and improve things we find along the way. |
If this is merged and goes into 0.25.0, it should be documented. Someone else can jump in if @kostya can't do that. I didn't have time to look at it, yet If no-one else has actually tried this, it shouldn't be merged. I don't think there is a rush. If it's merged after 0.25.0 is released, that's not a big deal. It even allows us to move entirely to the annotation based serialization without needing support for 0.24.1. |
i added docs for json, if its ok, i copy it to yaml. |
src/json/next/serialization.cr
Outdated
@@ -2,6 +2,109 @@ module JSON | |||
annotation Field | |||
end | |||
|
|||
# The `JSON::Serializable` module is a better alternative to `JSON.mapping`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not refer to this as a replacement for JSON.mapping
, since JSON.mapping
will probably go away before 1.0 if this works out. Better to provide a full description of what this code attempts to do, which is provide automatic serialization of the object to/from json.
src/json/next/serialization.cr
Outdated
# | ||
# class Location | ||
# include JSON::Serializable | ||
# property lat : Float64 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is probably a perfect place to show off @[JSON::Field(key: "lat")]
and rename this property latitiude
. Same for longitude
.
src/json/next/serialization.cr
Outdated
# | ||
# ### Usage | ||
# | ||
# `JSON::Serializable` use to serialize all instanse variables (but any of them can be excluded). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about
Including
JSON::Serializable
will create#to_json
andself.from_json
methods on the current class, and a constructor which takes aJSON::PullParser
. By default, these methods serialize into a json object containing the value of every instance variable, the keys being the instance variable name. Most primitives and collections are supported (string, integer, array, hash, etc.), along with objects which defineto_json
and a constructor taking aJSON::PullParser
. Union types are also supported, including unions with nil. If multiple types in a union parse correctly, it is undefined which one will be chosen.
src/json/next/serialization.cr
Outdated
# that accepts a `JSON::PullParser` and returns an object from it. Union types are supported, | ||
# if multiple types in the union can be mapped from the JSON, it is undefined which one will be chosen. | ||
# | ||
# To guide the serialization are using property annotation `JSON::Field`, example: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To change how individual instance variables are parsed and serialized, the annotation
JSON::Field
can be placed on the instance variable. Annotatingproperty
,getter
andsetter
macros is also allowed.
src/json/next/serialization.cr
Outdated
# end | ||
# ``` | ||
# | ||
# Supported field properties: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JSON::Field
properties
src/json/next/serialization.cr
Outdated
# | ||
# Supported field properties: | ||
# * **ignore**: if `true` skip this field in seriazation and deserialization (by default false) | ||
# * **key**: the property name in the JSON document (as opposed to the property name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the value of the key in the json object (by default the name of the instance variable)
src/json/next/serialization.cr
Outdated
# * **presense**: if `true`, a `@{{key}}_present` instance variable will be generated when the key was present (even if it has a `null` value), `false` by default | ||
# * **emit_null**: if `true`, emits a `null` value for nilable property (by default nulls are not emitted) | ||
# | ||
# Deserialization also respect default values of variables: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
respects
src/json/next/serialization.cr
Outdated
# A.from_json(%<{"a":1}>) # => A(@a=1, @b=1.0) | ||
# ``` | ||
# | ||
# When JSON::Serializable included it basically defines a constructor accepting a `JSON::PullParser` that reads from |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this paragraph
src/json/next/serialization.cr
Outdated
# | ||
# ### Extensions: `JSON::Serializable::Strict` and `JSON::Serializable::Unmapped`. | ||
# | ||
# If module `JSON::Serializable::Strict` included, unknown properties in the JSON |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the module ... is included, ...
src/json/next/serialization.cr
Outdated
# If module `JSON::Serializable::Strict` included, unknown properties in the JSON | ||
# document will raise a parse exception. By default the unknown properties | ||
# are silently ignored. | ||
# If module `JSON::Serializable::Unmapped` included, unknown properties in the JSON |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
will be stored in a
Hash(String, JSON::Any)
. On serialization, any keys insidejson_unmapped
will be serialized appended to the current json object.
src/json/next/serialization.cr
Outdated
# property lng : Float64 | ||
# | ||
# @[JSON::Field(key: "lat")] | ||
# property latitiude : Float64 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: => latitude
src/json/next/serialization.cr
Outdated
# include JSON::Serializable | ||
# | ||
# @[JSON::Field(key: "lat")] | ||
# property latitiude : Float64 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: => latitude
src/json/next/serialization.cr
Outdated
# | ||
# @[JSON::Field(key: "lng")] | ||
# property longitude : Float64 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Too much replace ^^
it should still be longitude
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just this, then copy for YAML.
src/json/next/serialization.cr
Outdated
annotation Field | ||
end | ||
|
||
# The `JSON::Serializable` is module which provide automatic serialization of the object to/from json. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
JSON::Serializable
module automatically generates methods for JSON serialization when included.
src/json/next/serialization.cr
Outdated
# | ||
# ### Usage | ||
# | ||
# Including JSON::Serializable will create #to_json and self.from_json methods on the current class, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs to regain `code`
and down the page.
src/json/next/serialization.cr
Outdated
# Including JSON::Serializable will create #to_json and self.from_json methods on the current class, | ||
# and a constructor which takes a JSON::PullParser. By default, these methods serialize into a json | ||
# object containing the value of every instance variable, the keys being the instance variable name. | ||
# Most primitives and collections are supported (string, integer, array, hash, etc.), along with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
supported as instance variable values
src/json/next/serialization.cr
Outdated
# | ||
# ### Extensions: `JSON::Serializable::Strict` and `JSON::Serializable::Unmapped`. | ||
# | ||
# If module `JSON::Serializable::Strict` included, unknown properties in the JSON |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the
JSON::Serializable::Strict
module is included, ...
src/json/next/serialization.cr
Outdated
# If module `JSON::Serializable::Strict` included, unknown properties in the JSON | ||
# document will raise a parse exception. By default the unknown properties | ||
# are silently ignored. | ||
# If module `JSON::Serializable::Unmapped` included, unknown properties in the JSON |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
i rebased and squashed, i think this is done. |
I would rather merge this after 0.25. I haven't review it yet. But we will link the PR as an example of user defined annotation and have a bit more of feedback/time to review. |
What's the benefit of merging after 0.25.0? |
We already have annotations in this release, so having |
Not having this as part of 0.25 release would probably result in much less feedback. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, let's include them but, as followups:
- ./next should be collapsed after release together with the Crystal::VERSION mention
- I would've imagine that a common field serialization declaration would come handy
- It would be great to rewrite mapping in terms of serializers. It would reduce the burden of keeping everything in sync. I think the mapping API is nice and has value.
At this point I'm thinking I think With that, I'd also like to remove user-defined annotations for now because they exhibit the problem in #6663. The language was working fine and it was simpler without all of this. We can revisit annotations later. Thoughts? |
Experimenting is good, I'm sure we'll end with a good solution for serialization. An option is to put annotation behind a flag instead of removing them, because they may be added again later. |
My thought would be to open a new issue for this instead of reviving an old PR. |
@asterite the reason we added I think that solving those problems is harder than solving #6663. I think that resolving type paths in the scope in which they were originally defined instead of when they are pasted is a good default and doable. |
extracted from: https://gist.github.com/asterite/db0ee7947d7eceb17c894fabffffafc4
added extra fields working: 1bb4ffc
specs pass local when i comment lines
if compare_versions(Crystal::VERSION, "0.25.0") >= 0
btw formatter just crashed:
./bin/crystal tool format spec/std/json/serializable_spec.cr
and i bad at writing docs.
i also extract strict and extra to modules, as it common behaviour: 824c293