Open FCO opened 3 years ago
Good idea,
I think I would probably move the definition of which attributes to the is json
trait, So there would be something like:
class Bla does JSON::Class[:opt-in] {
has $.private;
has $.internal is json<internal>;
has $.external is json;
}
Where they are essentially tags that must be specified for that attribute to serialised (no tag meaning unconditionally serialised,) possibly allowing a list.
I like the dynvar because it, e.g., would allow selecting attributes on some "authorisation context" (so some logged in users get some fields and others don't,) but I'd need to have a play with the body serializer to see how that would work best. I'd probably allow for both dynvar and argument to to-json
for some flexibility.
I'll have a think about this, but definitely something that is worth pursuing.
Cheers.
About the tags on is json
, I like it, but I also think it would be good to have the chance to define that outside. A class don't need to know every way it's going to be used, right?
Oh, I've just realised what you meant, I failed to realise that you literally meant to pass the names of the attributes :rofl:
That's doable but may run into difficulty where there are nested objects, one level is easy, I guess for subsequent levels it would be something like pass a pair in the list of attributes (the value being a list, etc...)
Hum! That makes completely sense, I haven't thought about that. Would that be feasible using json-path or json-pointer? I can totally see it as:
$post.to-json: <$.tiltle $.body $.author.* $.tags.* ˜.comments.*.body .comments.*.author.name>
but maybe that's too much...
another option would be:
my %*JSON-Class-opt-in{Post} = <title body author tags comments>;
my %*JSON-Class-opt-in{Person} = <name>;
my %*JSON-Class-opt-in{Comment} = <body author>;
$post.to-json
but maybe too complex also... and we could not differentiate the post's author from the comment's author.
What if?
my %*JSON-Class-opt-in{Post} = %( :title, :body, :author<external>, :tags<all>, :comments{ :body, :author{ :name } } ) # external is that previous discussed tag and all would be a uto generated tag that includes all attributes
# so
%*JSON-Class-opt-in{Bla} = "all"; # Would expose everything
%*JSON-Class-opt-in{Ble} = "external"; # Would expose only the attributes marked as external
%*JSON-Class-opt-in{Bli} = :attr; # or %( :attr ); Would expose only the attribute `attr`
%*JSON-Class-opt-in{Blo} = :blu{ :attr }; # Would expose only the attribute `attr` from the attribute `blu`
What do you think?
And just to let it clear:
class Bla { has $.a }
class Ble { has Bla @.as }
Bla.from-json(%( :123a )).to-json: :opt-in{ :a }; # Would return { "a": 123 }
# and
Ble.from-json(%( [:1a, :2a, :3a])).to-json: :opt-in{ :as{ :a } }; # Would return {"as": [{ "a": 1 }, { "a": 2 }, { "a": 3 }]}
Works for me, I'd probably leave the "all" case out as the default would be to serialise all the attributes that would be serialised as the code is now (either all those without json-skip
without opt-in or all those with json
or some other trait for the opt-in
case.)
I do see them as slightly orthogonal things though, "group opt-in" and "explicit attribute select".
BTW most of the changes would be in the JSON::Marshal
not here.
I was wondering, if I have a class that I want to use in 2 different places, but one is a external route and the other is an internal one. Then probably on both places I would want to "export" different attributes. Would have a way to do something like:
I personally like the dynvar one because if I define it on my route, doesn't matter what I do with the object, it will never leak data... Or something like that? If it already has something like that, I'm sorry.