Как использовать отражение Scala для получения свойства из аннотации метода

Итак, на данный момент у меня есть следующее:

import com.wordnik.swagger.annotations.ApiModel
import reflect.runtime.universe._

case class Model(
                  name: String,
                  qualifiedType: String,
                  properties: Map[String, ModelProperty],
                  description: Option[String] = None)

case class ModelProperty(
                          `type`: String,
                          qualifiedType: String,
                          description: Option[String] = None,
                          items: Option[ModelRef] = None)

case class ModelRef(
                     `type`: String,
                     ref: Option[String] = None,
                     qualifiedType: Option[String] = None)


class ModelHelper {

  def apply(models: Seq[Class[_]]) : Seq[Model] = {
    models.map(m =>
      Model(m.getSimpleName, m.getName, null, annotations(m))
    )
  }

  private def annotations(c : Class[_]) : Option[String] =
    c.getAnnotations.toSeq.map {
      case api : ApiModel => api.description
    }.headOption

  private def getType[T](clazz: Class[T]): Type = {
    val r = runtimeMirror(clazz.getClassLoader)
    r.classSymbol(clazz).toType
  }

  private def properties(c : Class[_]) = {
     getType(c).members.view.filter{!_.isMethod}.map { m =>
       m.annotations.map { a =>
         if(a.tree.tpe =:= typeOf[ApiModelProperty]) {
           a.tree.tpe.??? //<--stuck here
         }
       }
     }
  }
}

В def annotations() я могу получить все поля описания из аннотации класса ApiModel. В properties я хочу получить поля из ApiModelProperty.

В консоли SBT я четко вижу нужную аннотацию, если углублюсь:

...
scala> t.members.view.filter{!_.isMethod}map{ m => { m.annotations }}).head.head.tree
q: reflect.runtime.universe.Tree = new com.wordnik.swagger.annotations.ApiModelProperty @scala.annotation.meta.field(value = "A list of errors on the asset in five different containers.")

... но я не уверен, что сопоставить, чтобы получить экземпляр этой конкретной аннотации, как я сделал в annotations() выше. Я думаю, что мне действительно не нужен экземпляр, мне просто нужно получить value. Как мне это сделать? Я на Скала 2.11.8.


person djsumdog    schedule 02.03.2017    source источник


Ответы (1)


В итоге я нашел решение.

  private def properties(c: Class[_]): Map[String, ModelProperty] = {
    getType(c).members.view.filter {!_.isMethod}.map { m =>
      m.name.toString -> ModelProperty(
        toUsefulType(m.typeSignature.toString),
        m.typeSignature.toString,
        m.annotations.find { a =>
          a.tree.tpe =:= typeOf[com.wordnik.swagger.annotations.ApiModelProperty] || 
          a.tree.tpe =:= typeOf[io.swagger.annotations.ApiModelProperty] }.flatMap { b =>
            b.tree.children.flatMap { c =>
              c.collect {
                case Literal(Constant(value)) => value
              }
            }.headOption
          }.getOrElse("").toString
      )
    }.toMap
  }

Использованная литература:

Veeb's Brain Dump: Отражение аннотаций в Scala 2.10 — в этом сообщении блога используются некоторые устаревшие вызовы, которые я обновил в своем ответе для Scala 2.11.

Ответы/комментарии для Доступ к значению аннотации в Scala также оказался полезным.

person djsumdog    schedule 04.03.2017