2013-10-01 9 views
6

Ho un oggetto che assomiglia a questo:Come posso ottenere tutti gli oggetti vals e subobject vals usando reflection in Scala?

object Settings { 
    final val Host = "host" 
    final val Protocol = "protocol" 

    object User { 
    final val Name = "username" 
    final val Password = "password" 
    } 

    object Subject { 
    final val Query = "query" 
    final val Predicate = "predicate" 
    } 
} 

Quello che mi piacerebbe fare è qualcosa di simile membersAsHash(classOf[CollectionSettings]) e ricevere un hash) di tutte le vals che ho dichiarato nell'oggetto:

[ 
    Host => "host", 
    Protocol => "protocol", 
    Name => "username", 
    Password => "password", 
    Query => "query", 
    Predicate => "predicate" 
] 

Sarebbe perfetto se la chiave fosse una stringa, anche il nome completo del pacchetto (ad esempio com.example.Settings.User). Quello di cui ho veramente bisogno sono i valori, quindi se posso ottenerlo, è ancora accettabile.

questo mi ha preso il nome degli oggetti secondari, ma io non riesco a capire come ottenere i vals che sono interni a ciascuno:

val optionsToCheck = { 
    import scala.reflect.runtime.{universe => ru} 
    val mirror = ru.runtimeMirror(getClass.getClassLoader) 
    val subObjects = ru.typeOf[CollectionSettings.type].declarations.filter(_.isModule) 
    subobjects.map(o => mirror.reflectModule(o.asModule).instance.asInstanceOf[Object].toString).toList 
} 

risposta

8

La cosa accurata è che sei utilizzando definizioni delle costanti di valore (ad esempio, i valori finali con nessun tipo di annotazione, vedi §4.1 del language specification), in modo che non hai nemmeno bisogno di tutti gli specchi:

def deepMembers[A: scala.reflect.runtime.universe.TypeTag](a: A) = { 
    import scala.reflect.runtime.universe._ 

    def members(s: Symbol): Map[String, String] = 
    s.typeSignature.declarations.collect { 
     case m: ModuleSymbol => members(m) 
     case m: MethodSymbol if m.isAccessor => m.returnType match { 
     case ConstantType(Constant(s: String)) => Map(m.name.decoded -> s) 
     case _ => Map.empty[String, String] 
     } 
    }.foldLeft(Map.empty[String, String])(_ ++ _) 

    members(typeOf[A].termSymbol) 
} 

funziona in questo modo:

scala> deepMembers(Settings) foreach println 
(Name,username) 
(Predicate,predicate) 
(Query,query) 
(Password,password) 
(Protocol,protocol) 
(Host,host) 

Se per qualche motivo non era possibile utilizzare le definizioni delle costanti di valore, avresti bisogno di regolare il caso MethodSymbol di lavorare con gli specchi di istanza, ma l'approccio di base alla raccolta in modo ricorsivo coppie chiave-valore da oggetti figlio sarebbe essere lo stesso.

+0

Questo è stato super efficace. Grazie! –

+0

@TravisBrown: come posso farlo funzionare per i membri non finali? –

+1

@VenkatSudheerReddyAedama Probabilmente merita una nuova domanda: se ne pubblichi uno posso provare a dare un'occhiata stasera. –

Problemi correlati