18

A project needs to use the following combinaison of Jackson annotations together a lot. So, is there a way to create another annotation to avoid ugly copy/paste:

public class A {
    @JsonProperty("_id")
    @JsonSerialize(using=IdSerializer.class)
    @JsonDeserialize(using=IdDeserializer.class)
    String id;
}

public class B {
    @JsonProperty("_id")
    @JsonSerialize(using=IdSerializer.class)
    @JsonDeserialize(using=IdDeserializer.class)
    String id;
}

public class C {
    @CustomId // don't repeat that configuration endlessly
    String id;
}

Update: I've tried this, without success :-(

@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonProperty("_id")
@JsonSerialize(using=IdSerializer.class, include=JsonSerialize.Inclusion.NON_NULL)
@JsonDeserialize(using=IdDeserializer.class)
public @interface Id {}

public class D {
    @Id
    private String id;
}
yves amsellem
  • 7,106
  • 5
  • 44
  • 68

2 Answers2

29

The use of @JacksonAnnotationsInside solve the problem:

public class JacksonTest {

    @Retention(RetentionPolicy.RUNTIME)
    @JacksonAnnotationsInside
    @JsonProperty("_id")
    @JsonSerialize(using=IdSerializer.class, include=Inclusion.NON_NULL)
    @JsonDeserialize(using=IdDeserializer.class)
    public @interface Id {
    }

    public static class Answer {
        @Id
        String id;
        String name;

        public Answer() {}
    }

    @Test
    public void testInside() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        VisibilityChecker<?> checker = mapper.getSerializationConfig().getDefaultVisibilityChecker();
        mapper.setVisibilityChecker(checker.withFieldVisibility(JsonAutoDetect.Visibility.ANY));

        String string = "{ 'name' : 'John' , '_id' : { 'sub' : '47cc'}}".replace('\'', '"');
        Answer answer = mapper.reader(Answer.class).readValue(string);
        Assertions.assertThat(answer.id).isEqualTo("47cc");
    }
}
yves amsellem
  • 7,106
  • 5
  • 44
  • 68
1

I would guess that you could write your own annotation classes

package org.codehaus.jackson.annotate ;

public @ interface JsonProperty
{
      String value ( ) default "_id" ;
}

public @ interface JsonSerialize
{
      Class using ( ) default IdSerializer.class ;
}

...

Compile these classes and make sure that are in your classpath before the original versions. This reduces but does not eliminate the copy/paste.

Then your code sample becomes

public class A {
    @JsonProperty
    @JsonSerialize
    @JsonDeserialize
    String id;
}

public class B {
    @JsonProperty
    @JsonSerialize
    @JsonDeserialize
    String id;
}

I realize it is not really what you wanted, but it is a start.

emory
  • 10,725
  • 2
  • 30
  • 58
  • I don't get it, can you please provide an example of what it would look like? Thanks. – yves amsellem Oct 16 '12 at 20:05
  • Ok. It's seems to be a first interesting step. Any idea to gather only Serializer & Deserializer? Thanks a lot. – yves amsellem Oct 16 '12 at 21:04
  • @yvesamsellem I don't understand your comment. – emory Oct 16 '12 at 22:33
  • I understand your point. Even if it's not perfect, it will be helpful. I was just wondering if Jackson has a way — maybe an annotation — to group `@Serialize` and `@Deserialize` infos. Those are linked, and, IMO, they rarely come one without the other. Thanks again. – yves amsellem Oct 16 '12 at 22:45
  • @yvesamsellem so IdSerializer.class is not a suitable default, because elsewhere you might use a different serializer, but everywhere you use Serialize you also use Deserialize with the same using value. I don't know if Jackson has such a thing but you could use a custom class loader with something like BCEL to programatically add the Serialize and Deserialize annotations. – emory Oct 17 '12 at 00:01