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

Auto-generate Jackson (de)serializers #51

Open
AndreasVoellmy opened this issue Sep 16, 2016 · 10 comments
Open

Auto-generate Jackson (de)serializers #51

AndreasVoellmy opened this issue Sep 16, 2016 · 10 comments

Comments

@AndreasVoellmy
Copy link

It would be nice to automatically generate Jackson serialization and deserialization code for Derive4J defined ADTs. The encoding seems fairly straightforward: for any subclass, serialize its attributes int a JSON object and additionally add a "type" field that is a string encoding of the variant name (i.e. the tag).

There are probably some corner cases, like if a subclass has an attribute named "type". So there may need to be an option to define the name of the field that holds the tag. The default could be "type".

This of course should be optional (opt-in), so users who don't want this code generated are not affected.

@jbgi
Copy link
Member

jbgi commented Sep 16, 2016

Yeah! I have been thinking using JSON Processing for this. Would it suit your need?

@AndreasVoellmy
Copy link
Author

That sounds reasonable, but I'm not sure about the details. It looks like Jackson does not directly support JSR-353 (http://stackoverflow.com/questions/27982463/do-jackson-and-gson-directly-implement-the-standard-jsr-353), but there seems to be an adaptor that does implement SR-353 on Jackson: https://github.com/pgelinas/jackson-javax-json

@j-baker
Copy link

j-baker commented Nov 13, 2016

I took a look at this. Jackson is actually expressive enough on its own to handle this pretty much out of the box with Java 8 (if you're using https://github.com/FasterXML/jackson-module-parameter-names/tree/2.7).

You'd have to annotate your abstract class with @JsonTypeInfo and @JsonSubtypes, but that's pretty standard.

However, there's one slight snag; the implementation classes are all marked private, which means you can't reference them using @JsonSubtypes.

I can't find a way to override this. That being the case, completing this feature seems to be as easy as making it so that the implementation classes for the cases can be made package-private?

@j-baker
Copy link

j-baker commented Nov 13, 2016

To give an example, you'd do something like:

@JsonTypeInfo(id=NAME, as=PROPERTY, property="type")
@JsonSubtypes({@Type(value=MyCases.Foo.class, name="foo"), @Type(value=MyCases.Bar.class, name="bar"), @Type(value=MyCases.Baz.class, name="baz")})
public abstract class MyCase {
    public interface Cases<R> {
        R foo(Ham ham);
        R bar(Eggs eggs, Bacon bacon);
        R baz(Spam spam); 
    }

    abstract R match(Cases<R> cases);
}

and then if you ran your compiler with the '-parameters' command line flag, you'd be able to use a Jackson object mapper to do (in Jackson 2.8)

    ObjectMapper objectMapper = new ObjectMapper();
    MyCase myCase = objectMapper.readValue("{\"type\": \"ham\", \"eggs\": 25, \"bacon\": true}", MyCase.class);

and it'd be beautiful and straightforward.

But, you can't, because MyCases.Foo is a private class and I can't figure out how to make it package-private.

@danslapman
Copy link

I agree it might be useful to make case-classes optionally package-private for serialization

@jbgi
Copy link
Member

jbgi commented Mar 2, 2017

The internal representation of of the "case-classes" is not specified, and it could be changed (unlikely, but eg. to use a single array for all fields, if that improve performance).
For that reason, and also because runtime-reflection is unsafe, derive4j will not provide direct support of any usage based on runtime reflection. A compile-time only solution is what I'm working on.

However in the next release, there will be an extension api that will allow to make arbitrary modifications to the generated code, so users will be able to make changes, like using package-private visibility, by them-self.

@gneuvill
Copy link
Contributor

gneuvill commented Mar 2, 2017

For that reason, and also because runtime-reflection is unsafe, derive4j will not provide direct support of any usage based on runtime reflection. A compile-time only solution is what I'm working on.

Hurray !

@j-baker
Copy link

j-baker commented Apr 12, 2017

hey - i'd urge you to reconsider on this, just because Jackson is definitely the standard for object json serialization in java, it handles and it handles nested objects well, e.g. I can have a User with a UserType in, and Jackson will serialize it all properly.

You can provide Jackson support without runtime reflection if you e.g. write a 'Serializer' class; but frankly deserializing random json is always going to be doing something very much akin to runtime reflection no matter how you do it, and it's really quite easy to maintain Jackson support (my last proposal was just the most hacky).

If it makes any difference, jackson will go ahead, do the runtime reflection, and then generate efficient bytecode to run which actually does the serialization.

@jbgi
Copy link
Member

jbgi commented Apr 12, 2017

I have been making progress on a compile-time solution. Still not finished yet.
In the mean time I will soon do a release that allow arbitrary modifications on generated code. It should allows to implements the hack above.
Be warned though that It will not work for lazy values.

@jbgi
Copy link
Member

jbgi commented May 1, 2017

@AndreasVoellmy, @j-baker, @danslapman : 0.11.0 has been published with an extension api that allows arbitrary modifications on generated code. See example in #68 for removing private modifier (potentially to be adapted to act conditionally on the presence of a Jackson annotation).

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

No branches or pull requests

5 participants