如何创建现有注释的快捷方式?

时间:2015-01-27 15:26:38

标签: java scala annotations

在我的代码中,我多次使用以下注释:

@JsonSerialize(using = classOf[CustomColorRGBASerializer])

为了保持我的代码简短和DRY,我想创建一个这样的快捷方式,例如:

class JsonSerializeARGB
  extends @JsonSerialize(using = classOf[CustomColorRGBASerializer])

然后我可以将其用作新的@JsonSerializeARGB注释

我可以使用注释,但我不知道如何定义它们,因此我的尝试看起来很幼稚而且明显不正确,但我希望它具有意义。

我已阅读How do you define an @interface in Scala?How to create annotations and get them in scala,但它们对我没什么帮助,因为我不想创建全新的注释,而是创建现有注释的“子类”。可以这样做吗?

如果没有Scala解决方案,可以用Java完成这样的事情吗? (我正在使用的Jackson annotations无论如何都是用Java定义的。)

4 个答案:

答案 0 :(得分:4)

我担心有no way to subtype annotation with Java(和Scala)语言机制。我认为唯一的解决方案是制作Scala macro with the annotation

对于Scala编译器,Macro annotations可以使用

Macro Paradise plugin。希望他们将被包含在Scala 2.13中。要为Macro Paradise配置SBT,您可能需要关注this question。还有一个useful example的项目正在利用宏观天堂。

我相信这可以做得更好(特别是DefDef匹配),但类似于这个的宏应该可以解决你的问题:

import scala.reflect.macros._
import scala.annotation.StaticAnnotation
import scala.language.experimental.macros

class JsonSerializeARGB extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro JsonSerializeARGBMacroImpl.impl
}

object JsonSerializeARGBMacroImpl extends JsonSerializeARGBMacro

class JsonSerializeARGBMacro {
  def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
      import c.universe._

      def modifiedDef(d: DefDef) = {
        val (mods, name, tparams, paramss, tpt, body) = try {
          val q"$mods def $name[..$tparams](...$paramss): $tpt = $body" = d
          (mods, name, tparams, paramss, tpt, body)
        } catch {
          case _: MatchError => c.abort(c.enclosingPosition, "Failed to match...")
        }

        //TODO there is a problem with modifiers
        c.Expr(q"""
          @JsonSerialize(using = classOf[CustomColorRGBASerializer])
          def $name[..$tparams](...$paramss): $tpt = $body
          """)
      }

      annottees.map(_.tree) match {
        case (d: DefDef) :: Nil => modifiedDef(d)
        case _ => c.abort(c.enclosingPosition, "Invalid annottee.")
      }
    }
 }

答案 1 :(得分:2)

看看Java,没有合理的方法可以做到这一点。 Annotations cannot be extended in current Java versions,所以最简单的方法失败了。另一种可能性是使用反射来替换所有出现的JsonSerializeARGB JsonSerialize,尽管这只能在运行时,而不是在编译时。然而,Java Reflection API仅支持reading annotations,而不是添加它们。

所以有两种理论方法:

  • 与编译的字节代码混淆,但没有人可以诚实地想要这样做。
  • 修改Jackson(或任何其他读取注释的库)以识别您的自定义JsonSerializeARGB注释。

我不熟悉Scala,所以我不知道那里是否还有其他选择。但是我怀疑Scala提供了添加或扩展Java没有的注释的方法。

答案 2 :(得分:1)

采取不同的方法。杰克逊支持以编程方式定义序列化程序。因此,您可以定义自己的注释,然后使用反射来查找带​​有注释的所有类,并添加序列化器映射。

ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule("MyModule", new Version(1, 0, 0, null))
// use reflections to find all classes with Annotation the
for (classWithAnnotation <- classesWithAnnotation) {
  simpleModule.addSerializer(classWithAnnotation, new CustomColorRGBASerializer());
}
mapper.registerModule(simpleModule);

答案 3 :(得分:1)

以下是我尝试使用fasterXML库进行操作的示例:

1。创建自己的CustomSerializer

import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.core.JsonProcessingException;

public class CustomSerializer extends JsonSerializer<CustomDTO> {

@Override
public void serialize(CustomDTO value, JsonGenerator gen,
        com.fasterxml.jackson.databind.SerializerProvider serializers)
        throws IOException,
        JsonProcessingException {
    gen.writeStartObject();
    gen.writeStringField("AccentColor", value.getAccentColor());
    gen.writeStringField("ButtonColor", value.getButtonColor());
    gen.writeEndObject();
}

}

2。创建注释以使用此CustomSerializer:

从Scala 2.11开始,这需要在Java中完成,因为在Scala中,目前无法定义具有运行时保留的注释。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;

@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = CustomSerializer.class)
public @interface JsonSeriliazerCustom {}

3。在CustomDTO或您的班级上使用此项,如下所示:

@JsonSeriliazerCustom
public class CustomDTO {

    private String buttonColor;
    private String accentColor;
    private String frontColor;

    public String getButtonColor() {
        return buttonColor;
    }

    public void setButtonColor(String buttonColor) {
        this.buttonColor = buttonColor;
    }

    public String getAccentColor() {
        return accentColor;
    }

    public void setAccentColor(String accentColor) {
        this.accentColor = accentColor;
    }

    public String getFrontColor() {
        return frontColor;
    }

    public void setFrontColor(String frontColor) {
        this.frontColor = frontColor;
    }


}

4。写下你的主要方法:

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
import com.opera.oss.core.dto.CustomDTO;

public class TestJson {

    public static void main(String[] args)
    {
        CustomDTO responseDTO = new CustomDTO();
        responseDTO.setAccentColor("red");
        responseDTO.setButtonColor("blue");
        responseDTO.setFrontColor("yellow");
        System.out.println("hey");
         ObjectMapper om = new ObjectMapper();
         VisibilityChecker<?> checker = om.getSerializationConfig().getDefaultVisibilityChecker();
            om.setVisibilityChecker(checker.withFieldVisibility(JsonAutoDetect.Visibility.ANY));

            try {
                System.out.println(om.writer().writeValueAsString(responseDTO));
            } catch (JsonProcessingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

    }
}

使用的库:fasterXML - 2.5.0版本 - jackson-core,jackson-data-bind和jackson-annotations