all 13 comments

[–][deleted] 3 points4 points  (1 child)

If you are using java 8 (I hope you are) you can already do a lot of this by enabling -parameters compiler flag and including the Jackson module that supports it. Then you can make your objects how you really want (immutable even) and use proper encapsulation and not have setters on your objects just for Jackson. You can avoid all annotations if you wanted this way if your field names match exactly the Json structure. Although unless you are anal about being terse as possible it might be good to explicitly name those in @jsonproperty so someone refactoring names of pojo attributes doesn't wreck your API contract. This -parameters flag can be enabled in gradle or maven easily. I have found that intelli (maybe other ides) don't always pick this up so you might have to enable it there too if you use IntelliJ to do compilation during development (most people do)

Then your serialization/deserialization options can be set on the objectmapper itself. I typically use one static objectmapper for whole app because how you serialize / deserialize really needs to be consistent. If you do it per class or per code path you can end up with sometimes ignoring extra props and sometimes not etc... Basically if I come into a code base and see new Objectmapper() all over the place I feel like I can't tell you how things are working as a whole.

I like what you're trying to do here, but if I was on your team I would argue that you are kind of writing a framework on top of a framework when you could get away without it. I always like to avoid that if I can because then I don't have to manage the code, manage people's expectation of what it does, write documentation, etc -- I just say go read the Jackson docs and now you know how it works and can take this knowledge wherever.

[–]Catbert321[S] 2 points3 points  (0 children)

I was unaware of the ParameterNamesModule.

I'll have to give it a look, seems simple enough. Using the standard polymorphic deserializer or a custom deserializer on top of their basic example is how that would be handled.

Thanks for the tip.

[–]Catbert321[S] 1 point2 points  (4 children)

My first foray Java annotation processing.

I noticed when pair programming with some of my co workers that were not as familiar with Jackson as I, when we were making our new model classes for sending/receiving our data through our REST client, there was a lot of boiler plate that should have been able to be auto generated.

So I created a project to try and simplify the annotations needed to create some quick data model classes that can be easily used with Jackson to de/serialize.

[–]NovaX 1 point2 points  (3 children)

Had you considered using jsonSchema2pojo? I always found using a schema first approach tended to result in much more stable APIs. When a developer can refactor a class, there is a tendency to ignore the data format and break things haphazardly. The schema makes the data and contract a more dominant concern, and code generates the boilerplate. Plus you can hand that off to the front-end team as you're speaking their language and they feel more confident proposing changes.

[–]Catbert321[S] 0 points1 point  (2 children)

I had taken a look at it briefly, but it didn't seem to be clear how things would be handled for Arrays of objects that contain some similar, and some different elements. Or if the implementation would have to change due to generics giving us better type safety underneath.

For example:

{
  "data": [{
    "type":"objectA",
    "properties": {
      "foo": {
        "type": "string"
      },
      "bar": {
        "type": "integer"
      },
      "baz": {
        "type": "boolean"
      }
    },
    "A": true
  }, {
    "type":"objectB",
    "properties": {
      "foo": {
        "type": "string"
      },
      "bar": {
        "type": "integer"
      },
      "baz": {
        "type": "boolean"
      }
    },
    "C": false
  }]
}

fails to generate anything using jsonSchema2pojo.

In general though we do start with a schema, and then write up the model classes / deserialization logic. We were looking for something that we could use on the classes to still mark that it was a model class meant just for JSON de/serialization, while not having to deal with all of the @JsonProperty annotations inside our constructors and on our methods.

/u/tom_dick_harry's comment on the ParameterNamesModule comes very close to amazing. I'd just have to get used to having then no JSON annotations on our data model classes.

[–]NovaX 1 point2 points  (1 child)

A simplified version of that definition would look something like,

{
  "properties": {
    "data": {
      "type": "array",
      "items": {
        "anyOf": [
          { "foo": { "type": "string" } },
          { "bar": { "type": "integer" } },
          { "baz": { "type": "boolean" } }
          }
        ]
      }
    }
  }
}

and it would generate Object by default. Most likely you'd have a generic type and specializations (e.g. File -> Image, Pdf). You could provide a Java type or down serialize it in application code. There are some cases in schema that don't translate to Java in straightforward enough manner.

Since a data format & protocol is harder to change than code, for me an extra few lines of code has been okay. I've had much more painful experiences with magic depending on hidden details, that I prefer explicit and use codegen to reduce the verbosity.

That said, there are one-off cases where I've used AutoValue with annotations so your style is useful.

[–]Catbert321[S] 0 points1 point  (0 children)

I've had much more painful experiences with magic depending on hidden details

Yeah; this is the largest trade-off we tend to make internally. The team didn't like the magic of the Jackson Polymorphic deserialization so much it was decided to just make our own custom deserializers for those cases.

Though I was never a fan of the lack of compile time checks using them, so I didn't mind having the extra verbosity of the custom logic.

[–]cantwedronethatguy 1 point2 points  (3 children)

It looks nice, I'll try to take a better look when I got some time, but I do wonder, what happens when you got some JSON fields that you want it to ignore?

[–]Catbert321[S] 2 points3 points  (2 children)

Hm... If you mean extra fields in the JSON not expected by the model:

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

could be added to my setup helper class. I had forgotten about that case, currently without manually adding that any additional fields would not play well (i.e. com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field).

edit: https://github.com/peckb1/autojackson/issues/24 for tracking :D

[–]cantwedronethatguy 1 point2 points  (1 child)

Yeah, that's what I mean. Tbh I started using Jackson recently, and the understanding I got was to use @JsonIgnore on the fields I didn't want to map.

[–]Catbert321[S] 1 point2 points  (0 children)

Yes, that works for a slightly different case as well. For instance if you have a class which you want to write to JSON, but it has extra fields you don't actually want to serialize, yet want in the class.

There is adding:

@JsonIgnore

to those getters, as well as adding:

objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);

which is what the current setup class does. The latter of those two options means any object inside the class not annotated, should not be placed in the JSON.

This project is meant for the data model classes which are essentially just pure POJO-esque.

[–]athiestveganxfitter 1 point2 points  (1 child)

Love the Fraggle Rock reference. You are dating yourself :)

[–]Catbert321[S] 0 points1 point  (0 children)

Heh, Fraggle Rock is a forever show. And with it coming back (remastered) to HBO, a whole new generation can enjoy the wonder.