0
Help us understand the problem. What are the problem?
Organization

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

TL;DR

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

本文

JsonGetterとは

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

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

やりたいこと

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

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を実装する1
  2. findNameForSerializationで、条件に一致していれば値を返すようにする

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

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;
    }
}

使い方

以下をシリアライズしてみます。

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}(プロパティの順番は不同?)が出力されます。
ここではSimpleModuleObjectMapperに登録する形で書いています。

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に対するfindCreatorAnnotationJsonAnyGetterに対するhasAnyGetterというように、アノテーションを付与したのと同じ挙動を実現するためのメソッドが用意されている場合も有ります。

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

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

  1. NopAnnotationIntrospectorをベースにしているのは、Jackson側で指定が有るためです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
0
Help us understand the problem. What are the problem?