【问题标题】:How to create a shortcut to existing annotation?如何创建现有注释的快捷方式?
【发布时间】:2015-03-26 06:36:17
【问题描述】:

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

@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 定义的)。

【问题讨论】:

标签: java scala annotations


【解决方案1】:

这是我试图用 fastXML 库实现您想要做的示例:

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。创建 Annotation 以使用此 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

【讨论】:

  • 我认为第 3 步和第 4 步很明显,没有必要将它们包含在答案中。我认为没有它们会更容易理解。
  • 是的,很明显。为了完整起见,我将其包含在此处。
【解决方案2】:

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

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】:

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

    Macro annotations 可用于 Scala 编译器的 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.")
          }
        }
     }
    

    【讨论】:

      【解决方案4】:

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

      所以有两种理论上的方法:

      • 弄乱编译后的字节码,但没有人真的愿意这样做。
      • 修改 Jackson(或任何其他读取注释的库)以识别您的自定义 JsonSerializeARGB 注释。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-01-07
        • 2023-01-02
        • 1970-01-01
        • 2021-02-03
        • 1970-01-01
        • 2017-10-29
        • 1970-01-01
        • 2015-04-27
        相关资源
        最近更新 更多