Skip to content

Commit

Permalink
add support for selective JSON and YAML serialization (crystal-lang#9567
Browse files Browse the repository at this point in the history
)

* add support for selective JSON and YAML serialization

* fix yaml serialization spec

* fix yaml serialization spec again

my bad for copy and pasting

Co-authored-by: Brian J. Cardiff <[email protected]>
  • Loading branch information
stakach and Brian J. Cardiff authored Oct 26, 2020
1 parent 40211f1 commit 2da8468
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 4 deletions.
25 changes: 25 additions & 0 deletions spec/std/json/serializable_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,21 @@ struct JSONAttrPersonWithYAMLInitializeHook
end
end

struct JSONAttrPersonWithSelectiveSerialization
include JSON::Serializable

property name : String

@[JSON::Field(ignore_serialize: true)]
property password : String

@[JSON::Field(ignore_deserialize: true)]
property generated : String = "generated-internally"

def initialize(@name : String, @password : String)
end
end

abstract class JSONShape
include JSON::Serializable

Expand Down Expand Up @@ -834,6 +849,16 @@ describe "JSON mapping" do
JSONAttrPersonWithYAMLInitializeHook.from_yaml(person.to_yaml).msg.should eq "Hello Vasya"
end

it "json with selective serialization" do
person = JSONAttrPersonWithSelectiveSerialization.new("Vasya", "P@ssw0rd")
person.to_json.should eq "{\"name\":\"Vasya\",\"generated\":\"generated-internally\"}"

person_json = "{\"name\":\"Vasya\",\"generated\":\"should not set\",\"password\":\"update\"}"
person = JSONAttrPersonWithSelectiveSerialization.from_json(person_json)
person.generated.should eq "generated-internally"
person.password.should eq "update"
end

describe "use_json_discriminator" do
it "deserializes with discriminator" do
point = JSONShape.from_json(%({"type": "point", "x": 1, "y": 2})).as(JSONPoint)
Expand Down
25 changes: 25 additions & 0 deletions spec/std/yaml/serializable_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@ class YAMLAttrPersonEmittingNull
property age : Int32?
end

struct YAMLAttrPersonWithSelectiveSerialization
include YAML::Serializable

property name : String

@[YAML::Field(ignore_serialize: true)]
property password : String

@[YAML::Field(ignore_deserialize: true)]
property generated : String = "generated-internally"

def initialize(@name : String, @password : String)
end
end

@[YAML::Serializable::Options(emit_nulls: true)]
class YAMLAttrPersonEmittingNullsByOptions
include YAML::Serializable
Expand Down Expand Up @@ -410,6 +425,16 @@ describe "YAML::Serializable" do
ex.location.should eq({3, 1})
end

it "works with selective serialization" do
person = YAMLAttrPersonWithSelectiveSerialization.new("Vasya", "P@ssw0rd")
person.to_yaml.should eq "---\nname: Vasya\ngenerated: generated-internally\n"

person_yaml = "---\nname: Vasya\ngenerated: should not set\npassword: update\n"
person = YAMLAttrPersonWithSelectiveSerialization.from_yaml(person_yaml)
person.generated.should eq "generated-internally"
person.password.should eq "update"
end

it "does to_yaml" do
person = YAMLAttrPerson.from_yaml("---\nname: John\nage: 30\n")
person2 = YAMLAttrPerson.from_yaml(person.to_yaml)
Expand Down
6 changes: 4 additions & 2 deletions src/json/serialization.cr
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ module JSON
#
# `JSON::Field` properties:
# * **ignore**: if `true` skip this field in serialization and deserialization (by default false)
# * **ignore_serialize**: if `true` skip this field in serialization (by default false)
# * **ignore_deserialize**: if `true` skip this field in deserialization (by default false)
# * **key**: the value of the key in the json object (by default the name of the instance variable)
# * **root**: assume the value is inside a JSON object with a given key (see `Object.from_json(string_or_io, root)`)
# * **converter**: specify an alternate type for parsing and generation. The converter must define `from_json(JSON::PullParser)` and `to_json(value, JSON::Builder)` as class methods. Examples of converters are `Time::Format` and `Time::EpochConverter` for `Time`.
Expand Down Expand Up @@ -158,7 +160,7 @@ module JSON
{% properties = {} of Nil => Nil %}
{% for ivar in @type.instance_vars %}
{% ann = ivar.annotation(::JSON::Field) %}
{% unless ann && ann[:ignore] %}
{% unless ann && (ann[:ignore] || ann[:ignore_deserialize]) %}
{%
properties[ivar.id] = {
type: ivar.type,
Expand Down Expand Up @@ -266,7 +268,7 @@ module JSON
{% properties = {} of Nil => Nil %}
{% for ivar in @type.instance_vars %}
{% ann = ivar.annotation(::JSON::Field) %}
{% unless ann && ann[:ignore] %}
{% unless ann && (ann[:ignore] || ann[:ignore_serialize]) %}
{%
properties[ivar.id] = {
type: ivar.type,
Expand Down
6 changes: 4 additions & 2 deletions src/yaml/serialization.cr
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ module YAML
#
# `YAML::Field` properties:
# * **ignore**: if `true` skip this field in serialization and deserialization (by default false)
# * **ignore_serialize**: if `true` skip this field in serialization (by default false)
# * **ignore_deserialize**: if `true` skip this field in deserialization (by default false)
# * **key**: the value of the key in the yaml object (by default the name of the instance variable)
# * **converter**: specify an alternate type for parsing and generation. The converter must define `from_yaml(YAML::ParseContext, YAML::Nodes::Node)` and `to_yaml(value, YAML::Nodes::Builder)` as class methods. Examples of converters are `Time::Format` and `Time::EpochConverter` for `Time`.
# * **presence**: 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
Expand Down Expand Up @@ -164,7 +166,7 @@ module YAML
{% properties = {} of Nil => Nil %}
{% for ivar in @type.instance_vars %}
{% ann = ivar.annotation(::YAML::Field) %}
{% unless ann && ann[:ignore] %}
{% unless ann && (ann[:ignore] || ann[:ignore_deserialize]) %}
{%
properties[ivar.id] = {
type: ivar.type,
Expand Down Expand Up @@ -270,7 +272,7 @@ module YAML
{% properties = {} of Nil => Nil %}
{% for ivar in @type.instance_vars %}
{% ann = ivar.annotation(::YAML::Field) %}
{% unless ann && ann[:ignore] %}
{% unless ann && (ann[:ignore] || ann[:ignore_serialize]) %}
{%
properties[ivar.id] = {
type: ivar.type,
Expand Down

0 comments on commit 2da8468

Please sign in to comment.