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

Allow inlining/unwrapping of child objects

    Details

    • Type: New Feature New Feature
    • Status: Closed Closed
    • Priority: Minor Minor
    • Resolution: Fixed
    • Affects Version/s: 1.1
    • Fix Version/s: None
    • Component/s: None
    • Labels:
      None
    • Number of attachments :
      0

      Description

      Although Jackson does not (and probably should not) do extensive structural transformations to objects during mapping, it would sometimes be useful to allow "unwrapping" (aka inlining) child objects. Such that, for example, something like:

      class Bean {
         String name;
         Point point;
      }
      

      could be serialized as

      {
        "name" : "...",
        "x" : 1,
        "y" : 1
      }
      

      instead of what is produced by default

      {
        "name" : "...",
        "point" : {
            "x" : 1,
            "y" : 1
        }
      } 
      

      assuming proper settings were used. An annotation-based (applicable to fields and methods; possibly to classes also) approach seems the obvious choice: this could be a new property of @JsonProperty, or a new annotation if need be.

        Activity

        Hide
        Marcel Oelke added a comment -

        Is it possible that this feature obeys somehow the @JsonIgnoreProperties(ignoreUnknown = true) setting?

        Because currently (trunk) a NullPointerException is thrown when I try to deserialize a json string into an object with a @JsonCreator constructor and a @JsonUnwrapped field.

        Note: only "id" is present in the creator, "name" is an optional attribute.

        See test below that reproduces the problem

         
         @Test
         public void testExtensions() throws SerializerException, JsonGenerationException, JsonMappingException, IOException {
             // create a new instance of, with a wrapped B inside.
             A a = new A(new B("test"));
             
             // serialize a, prints "{"name":"test"}" as expected.
             ObjectMapper mapper = new ObjectMapper();
             StringWriter w = new StringWriter();
             mapper.writeValue(w, a);
             String json = w.toString();
             System.out.println(json);
             
             // try to deserialize a A throws exception.
             A a2 =  mapper.readValue(json, A.class);
         }
         
         @JsonIgnoreProperties(ignoreUnknown = true)
         public static class A {
             private B b;
             
             public A(B b) {this.b = b;}
             
             @JsonUnwrapped
             public B getB() {
                 return b;    
             }
        
            // This creator is not working, a NullPointerException is thrown
             @JsonCreator
             public static A creator(@JsonProperty("id") String id) {
                 return new A( new B(id) );
             }
        
            // This creator works
        	//        @JsonCreator
        	//        public static A creator(@JsonProperty("id") String id, @JsonProperty("name") String name) {
        	//            return new A( new B(id) );
        	//        }
        
             
         }
         
         @JsonIgnoreProperties(ignoreUnknown = true)
         public static class B {
             String id;
             String name;
             
             public B(){}
             public B(String id) {this.id = id;}
             public String getId() { return name;}
             public String getName() { return name;}
             public void setName(String name) {this.name = name;}
         }
        
        Show
        Marcel Oelke added a comment - Is it possible that this feature obeys somehow the @JsonIgnoreProperties(ignoreUnknown = true) setting? Because currently (trunk) a NullPointerException is thrown when I try to deserialize a json string into an object with a @JsonCreator constructor and a @JsonUnwrapped field. Note: only "id" is present in the creator, "name" is an optional attribute. See test below that reproduces the problem @Test public void testExtensions() throws SerializerException, JsonGenerationException, JsonMappingException, IOException { // create a new instance of, with a wrapped B inside. A a = new A( new B( "test" )); // serialize a, prints "{" name ":" test "}" as expected. ObjectMapper mapper = new ObjectMapper(); StringWriter w = new StringWriter(); mapper.writeValue(w, a); String json = w.toString(); System .out.println(json); // try to deserialize a A throws exception. A a2 = mapper.readValue(json, A.class); } @JsonIgnoreProperties(ignoreUnknown = true ) public static class A { private B b; public A(B b) { this .b = b;} @JsonUnwrapped public B getB() { return b; } // This creator is not working, a NullPointerException is thrown @JsonCreator public static A creator(@JsonProperty( "id" ) String id) { return new A( new B(id) ); } // This creator works // @JsonCreator // public static A creator(@JsonProperty( "id" ) String id, @JsonProperty( "name" ) String name) { // return new A( new B(id) ); // } } @JsonIgnoreProperties(ignoreUnknown = true ) public static class B { String id; String name; public B(){} public B( String id) { this .id = id;} public String getId() { return name;} public String getName() { return name;} public void setName( String name) { this .name = name;} }
        Hide
        Tatu Saloranta added a comment -

        Yes, basically implementation has to accept all kinds of things first, as it can not quite determine required ones. This is mostly to support multiple unwrapped objects, without requiring deserializers to declare things they accept... it's a mess.
        This is very tricky feature to support, so there may be some edge cases;but i'll see what's the issue with the test case.

        Show
        Tatu Saloranta added a comment - Yes, basically implementation has to accept all kinds of things first, as it can not quite determine required ones. This is mostly to support multiple unwrapped objects, without requiring deserializers to declare things they accept... it's a mess. This is very tricky feature to support, so there may be some edge cases;but i'll see what's the issue with the test case.
        Hide
        Aner Perez added a comment -

        It would be nice if the JsonUnwrapped annotation had an optional "prefix" attribute which could be prepended to each property of the object being unwrapped. That way, if you had a parent object with two child objects of the same type, you could annotate them both with JsonUnwrapped without having a clash in attribute names.

        For example

        class Bean {
           String name;
           @JsonUnwrapped(prefix="start")
           Point start;
           @JsonUnwrapped(prefix="end")
           Point end;
        }

        could be serialized as

        {
          "name" : "...",
          "startx" : 1,
          "starty" : 1,
          "endx" : 99,
          "endy" : 99
        }

        This would also be useful if the child had an attribute with the same name as one of the parent's attributes.

        Show
        Aner Perez added a comment - It would be nice if the JsonUnwrapped annotation had an optional "prefix" attribute which could be prepended to each property of the object being unwrapped. That way, if you had a parent object with two child objects of the same type, you could annotate them both with JsonUnwrapped without having a clash in attribute names. For example class Bean { String name; @JsonUnwrapped(prefix="start") Point start; @JsonUnwrapped(prefix="end") Point end; } could be serialized as { "name" : "...", "startx" : 1, "starty" : 1, "endx" : 99, "endy" : 99 } This would also be useful if the child had an attribute with the same name as one of the parent's attributes.
        Hide
        Tatu Saloranta added a comment -

        Sounds like a good idea: can you create a separate feature request? The reason is that it'll be easier to track this; whether this can get in 1.9 depends on various factors.

        Show
        Tatu Saloranta added a comment - Sounds like a good idea: can you create a separate feature request? The reason is that it'll be easier to track this; whether this can get in 1.9 depends on various factors.
        Hide
        Aner Perez added a comment -

        I added feature request JACKSON-669 to track this

        Show
        Aner Perez added a comment - I added feature request JACKSON-669 to track this

          People

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

            Dates

            • Created:
              Updated:
              Resolved: