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

Possible re-work of JSON::Class #11

Open
vrurg opened this issue Aug 9, 2023 · 9 comments
Open

Possible re-work of JSON::Class #11

vrurg opened this issue Aug 9, 2023 · 9 comments

Comments

@vrurg
Copy link

vrurg commented Aug 9, 2023

There is a little but non-zero chance that I'd be working on an alternate implementation for JSON::Class because I need something more configurable and extensible. Unfortunately, JSON::Class -> JSON::Unmarshal/Marshal lacks necessary scalability. I'd like to get something akin to LibXML::Class and XML::Class.

The question is about naming. It would be great if the re-work follows the kind of standard naming approach and, yes, be named JSON::Class. With zef ecosystem this is not a big deal as the author name would be different. But I'd like to synchronize the matter with you and know your opinion on this.

Alternatively, I can simply re-implement this module and submit a PR.

Once again, it is all in limbo now. I have an immediate solution for my current problem in mind, even though it requires either to somehow make marshal return raw structure (hash, list, whatever), or de-deserialize the JSON it produces (slow!).

@jonathanstowe
Copy link
Owner

I think broadly I concur with your take, historically I made JSON::Class the way it is currently because JSON::Unmarshal already existed and that was half the problem solved and I could move on and make the things I wanted to make with it.

Having the functionality split across three or more modules also poses a logistical problem as well as a technical one, there are definitely features that I have postponed or only partially completed due to the need for symmetrical changes in JSON::Unmarshal/JSON::Marshal and so forth.

Also there are things in e.g. JSON::Unmarshal/JSON::Marshal which are not specific to the (de-)serialization of JSON but which functionality is hidden away in a way that makes re-use difficult, hence I wound up re-implementing parts of both for e.g. MessagePack::Class etc and is behind the issue that you describe.

So in general I'd be open to re-working JSON::Class such that it implements the Object to Structure round trip itself, but I'd suggest abstracting the non-JSON specific parts to a separate Role such that is can be re-used by e.g. MessagePack::Class, a possible YAML::Class or any other thing where the representation can map to some native data structure in Raku.

It might be worth starting with a PoC that basically refactors J::M/J::UM into a role with more public methods and see how that works ...

@Xliff
Copy link

Xliff commented Aug 25, 2023

And here I was about to submit a PR that allows one to define a json-unmarshal method an attribute can compose, that will allow one to do away with repeated is unmarshalled-by traits for the same type. I have an immediate need, so I'll probably be submitting it anyways.

@jonathanstowe
Copy link
Owner

Do it anyway, as who knows when this will happen. My personal preference is not specially named methods, but rather some trait that indicates the purpose of the method (so something like is unmarshal-for(Type) or such...)

@Xliff
Copy link

Xliff commented Aug 25, 2023

@jonathanstowe - Well, then you might not like my quick and dirty solution, which allowed me to write a single role to handle a dozen attributes without having to write the same is unmarshaled-by statement for each. What I did was use a custom trait is json-date-time, which applies a role to the associated attribute, which then adds the .json-unmarshal method that is called by JSON::Unmarshal if it exists.

I'm all for the is unmarshal-for(Type) mechanism, if you can tell me how it allows for reuse.

Thanks

@Xliff
Copy link

Xliff commented Aug 25, 2023

After thinking about it a bit longer, I do see where you are going with the is unmarshal-for(Type) option, however I don't think it provides enough distinction for things like:

  • Proper handling between different formats (one use case for me is using both XML::Class and JSON::Class on the same object and they have requirements that differ by format)
  • Might prove difficult to use if requirements for Type differ across classes in the same project.

Those are my initial thoughts. Please let me know if you have any suggestions.

@jonathanstowe
Copy link
Owner

I'd see it as doing something like:

has DateTime $.my-date;

method unmarshal-datetime(Str $d --> DateTime ) is unmarshal-for(DateTime) {
....
}

Then the unmarshaller can inspect the methods to find those which do some role that that the trait has added and match the type supplied to the type of the attribute being unmarshalled. Arguably the specific type could be omitted and it could just match the return type of the method. It could even be a multi with different subsets or where clauses to handle different cases of the input format.

A method defined like that can be re-used by putting it in a role and consuming that.

@jonathanstowe
Copy link
Owner

  • Proper handling between different formats (one use case for me is using both XML::Class and JSON::Class on the same object and they have requirements that differ by format)

The methods can have multiple candidates with arguments constrained by subset for example.

  • Might prove difficult to use if requirements for Type differ across classes in the same project.

I'm pretty sure any scheme would have this feature if implemented in a monolithic fashion. There is no reason to stop you from having separate roles for different requirements, or just a refinement of the multi candidates.

@Xliff
Copy link

Xliff commented Aug 27, 2023

  • Proper handling between different formats (one use case for me is using both XML::Class and JSON::Class on the same object and they have requirements that differ by format)

Given the example here:

has DateTime $.my-date;

method unmarshal-datetime(Str $d --> DateTime ) is unmarshal-for(DateTime) {
....
}

How would unmarshal-datetime know how to handle the difference between XML data and JSON data?

The methods can have multiple candidates with arguments constrained by subset for example.

I'm also having a little trouble seeing how subsets would help. Maybe a bit more elaboration into what you are describing?

@vrurg
Copy link
Author

vrurg commented Oct 18, 2023

After some consideration, I decided to give the ecosystem a stress-test and went on with my own JSON::Class implementation. Not publishing it yet because there are not docs yet; but, first of all, I'd like get in sync here. For example, modules depending on the current JSON::Class are better be given :auth<zef:jonathanstowe> in their dependencies first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants