【Jackson】AnnotationIntrospectorでJsonGetter同様の動作を実現する

# TL;DR
- `AnnotationIntrospector`には`JsonGetter`同様の動作を再現するための明示的なメソッドは用意されていない
- `findNameForSerialization`を実装することで、`JsonGetter`同様の動作を実現できる

# 本文
## JsonGetterとは
`Jackson`では、名前が`getter`に当たるメソッドしかシリアライズ対象になりません。
一方、`JsonGetter`アノテーションを付与したメソッドは名前に関わらずシリアライズ対象となります。

```java
// シリアライズすると{"foo":1,"bar":2}が出力される
class Src {
    public int getFoo() { return 1; }
    @JsonGetter public int bar() { return 2; }
}
```

## やりたいこと
`Jackson`の挙動をカスタムして、特定条件で`JsonGetter`同様の動作をさせます。
今回は以下の`MyAnnotation`が設定されていたメソッドであることを条件にします。

```java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
```

## やり方
以下のようにすることで実装できます。

1. `AnnotationIntrospector`を実装する[^nop]
2. `findNameForSerialization`で、条件に一致していれば値を返すようにする

[^nop]: `NopAnnotationIntrospector`をベースにしているのは、[`Jackson`側で指定が有るため](http://fasterxml.github.io/jackson-databind/javadoc/2.13/com/fasterxml/jackson/databind/introspect/NopAnnotationIntrospector.html#:~:text=Can%20be%20used%20as%20is%20to%20suppress%20handling%20of%20annotations%3B%20or%20as%20a%20basis%20for%20simple%20configuration%20overrides%20(whether%20based%20on%20annotations%20or%20not).)です。

以下は実装のサンプルです。

```java
import com.fasterxml.jackson.databind.PropertyName;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;

public class MyAnnotationIntrospector extends NopAnnotationIntrospector {
    @Override
    public PropertyName findNameForSerialization(Annotated a) {
        return _findAnnotation(a, MyAnnotation.class) != null ? PropertyName.USE_DEFAULT : null;
    }
}
```

### 使い方
以下をシリアライズしてみます。

```java
import com.fasterxml.jackson.annotation.JsonGetter;

class Src {
    public int getFoo() { return 1; }
    @JsonGetter public int bar() { return 2; }
    @MyAnnotation public int baz() { return 3; }
}
```

以下の`main`関数を実行すると、`{"baz":3,"foo":1,"bar":2}`（プロパティの順番は不同？）が出力されます。
ここでは`SimpleModule`を`ObjectMapper`に登録する形で書いています。

```java
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

public class Main {
    public static void main(String[] args) throws JsonProcessingException {
        SimpleModule simpleModule = new SimpleModule() {
            @Override
            public void setupModule(Module.SetupContext context) {
                super.setupModule(context);
                context.appendAnnotationIntrospector(new MyAnnotationIntrospector());
            }
        };

        ObjectMapper mapper = JsonMapper.builder()
                .addModule(simpleModule)
                .build();
        Src src = new Src();

        System.out.println(mapper.writeValueAsString(src));
    }
}
```

### 補足
アノテーションや特定名・クラスを条件に挙動をカスタムする場合、`AnnotationIntrospector`を実装することになります。
このクラスには、`JsonCreator`に対する`findCreatorAnnotation`、`JsonAnyGetter`に対する`hasAnyGetter`というように、アノテーションを付与したのと同じ挙動を実現するためのメソッドが用意されている場合も有ります。

https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind/latest/com/fasterxml/jackson/databind/AnnotationIntrospector.html

一方、`JsonGetter`に対してはそのようなメソッドが用意されていませんでした。
そこで`Jackson`側のコードを見た所、`JacksonAnnotationIntrospector`に実装が有りました。

https://github.com/FasterXML/jackson-databind/blob/aebeedfdc22e0068051a45c318b9ece838958a33/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java#L1068

この記事は上記コードを参考に書いています。