Jackson JSON Processor
  1. Jackson JSON Processor
  2. JACKSON-469

Support "builder pattern" for deserialization, @JsonCreator

    Details

    • Type: New Feature New Feature
    • Status: Resolved Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.7
    • Fix Version/s: 2.0.0
    • Component/s: None
    • Labels:
      None
    • Number of attachments :
      0

      Description

      (suggested by Dain the awesome)

      In addition to existing ways of using @JsonCreator, there is one obvious way to further improve support for deserializing immutable objects. While constructor-based deserialization works correctly, it becomes unwieldy when number of properties grows. One good way to get around this would be to support builder-style, in which a builder type has "setters" to call, which return an instance of builder (which may be the same builder or a new instance); and once all data has been passed, a build method is called to produce actual value instance.

      To do this, couple of things need to be added; for example:

      • A mechanism to indicate which builder type to use for building value types; can probably just add either a new annotation (@JsonBuild?) or add a property to @JsonDeserialize (.usingBuilder=MyBuilder.class). Since this is resolved using AnnotationIntrospector, it will be extensible for custom mechanisms as well (including non-annotation-based extensions)
      • A way to indicate "build" method (@JsonBuildMethod?)
      • Possibly ability to support setter-like methods with different naming convention? (I like withXxx naming convention, but I have no idea if anyone else uses things other than setXxx)

      I may be missing other pieces, but at least above need to be considered.

        Activity

        Hide
        Gordon added a comment -

        Found this issue as I was looking for a way to do exactly this and wanted to add a common Builder-pattern set method convention of just using the property name as the method name, so something like:

        public class MyBigClass {
            private final String myString;
            private final int myInt;
        
            private MyBigClass(Builder builder) {
                this.myString = builder.myString;
                this.myInt = builder.myInt;
            }
        
            public static class Builder {
                private String myString;
                private int myInt;
        
                public Builder myString(String myString) { this.myString = myString; return this; }
                public Builder myInt(int myInt) { this.myInt = myInt; return this; }
                public MyBigClass build() { return new MyBigClass(this); }
            }
        }
        
        Show
        Gordon added a comment - Found this issue as I was looking for a way to do exactly this and wanted to add a common Builder-pattern set method convention of just using the property name as the method name, so something like: public class MyBigClass { private final String myString; private final int myInt; private MyBigClass(Builder builder) { this .myString = builder.myString; this .myInt = builder.myInt; } public static class Builder { private String myString; private int myInt; public Builder myString( String myString) { this .myString = myString; return this ; } public Builder myInt( int myInt) { this .myInt = myInt; return this ; } public MyBigClass build() { return new MyBigClass( this ); } } }
        Hide
        Tatu Saloranta added a comment -

        Ok good suggestion – I am thinking that perhaps ability to specify prefix ("with", "") to trim would be simplest flexible-enough solution.
        And obviously build() method name should also be configurable but with sensible default ("build").

        As such, AnnotationIntrospector could support per-class annotation that indicates a class annotation – could either overload @JsonCreator so it could be used for classes too.
        Or if that is too confusing, either add "builder" property for @JsonDeserialize (buildUsing=Builder.class) or add yet another annotation.
        This will also allow custom introspection by sub-classing AnnotationIntrospector, same way as all default annotions works.

        After builder object is found, it can be introspected. And the real complexity is to pipe builder through BeanDeserializer and other parts... but it should be doable, just quite a bit of work.
        On plus side, I think this entry is highly voted for now, so should make it in 2.0 when we get it done.

        Show
        Tatu Saloranta added a comment - Ok good suggestion – I am thinking that perhaps ability to specify prefix ("with", "") to trim would be simplest flexible-enough solution. And obviously build() method name should also be configurable but with sensible default ("build"). As such, AnnotationIntrospector could support per-class annotation that indicates a class annotation – could either overload @JsonCreator so it could be used for classes too. Or if that is too confusing, either add "builder" property for @JsonDeserialize (buildUsing=Builder.class) or add yet another annotation. This will also allow custom introspection by sub-classing AnnotationIntrospector, same way as all default annotions works. After builder object is found, it can be introspected. And the real complexity is to pipe builder through BeanDeserializer and other parts... but it should be doable, just quite a bit of work. On plus side, I think this entry is highly voted for now, so should make it in 2.0 when we get it done.
        Hide
        James Roper added a comment -

        Wow, this would get rid of my need to @JsonCreator annotate all my immutable objects. Would @JsonCreator on a static inner class to indicate the builder work? Apart from that I like the builder property for @JsonDeserializer option. Also like the sensible default of build() for the method name.

        Show
        James Roper added a comment - Wow, this would get rid of my need to @JsonCreator annotate all my immutable objects. Would @JsonCreator on a static inner class to indicate the builder work? Apart from that I like the builder property for @JsonDeserializer option. Also like the sensible default of build() for the method name.
        Hide
        Tatu Saloranta added a comment -

        Implemented:

        • Indicate type of builder with @JsonDeserialize(builder=MyBuilder.class) on value class
        • Configure (optionally) Builder aspects with @JsonPOJOBuilder
        Show
        Tatu Saloranta added a comment - Implemented: Indicate type of builder with @JsonDeserialize(builder=MyBuilder.class) on value class Configure (optionally) Builder aspects with @JsonPOJOBuilder

          People

          • Assignee:
            Tatu Saloranta
            Reporter:
            Tatu Saloranta
          • Votes:
            7 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: