2013-10-01 4 views
6

Ich habe ein Objekt, das wie folgt aussieht:Wie kann ich alle Objektwerte und Subobjektwerte mithilfe von Reflektion in Scala erhalten?

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

Was ich möchte, ist wie membersAsHash(classOf[CollectionSettings]) etwas zu tun und einen Hash) aller der vals erhalten, die ich in dem Objekt deklariert haben:

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

Es wäre in Ordnung, wenn der Schlüssel eine Zeichenkette wäre, sogar der vollständige Paketname (zB com.example.Settings.User). Was ich wirklich brauche, sind die Werte. Wenn ich das also nur bekommen kann, ist das immer noch akzeptabel.

Das hat mir den Namen der Subobjekte bekommen, aber ich kann nicht scheinen, um herauszufinden, wie die vals zu erhalten, die zu jedem internen sind:

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 
} 

Antwort

8

Die hier nette Sache ist, dass Sie mit konstanten Wert Definitionen (dh Endwerte ohne Typanmerkung; § 4.1 des language specification sehen), so dass Sie nicht einmal alle Spiegel brauchen:

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

Es funktioniert wie folgt:

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

Wenn Sie aus irgendeinem Grund nicht konstanten Wert Definitionen verwenden könnte, dann würden Sie die MethodSymbol Fall anpassen müssen mit Instanz-Spiegel arbeiten, aber der grundlegende Ansatz rekursiv Schlüssel-Wert-Paare von untergeordneten Objekten zu sammeln würde sei gleich.

+0

Das war super effektiv. Vielen Dank! –

+0

@TravisBrown: Wie kann ich es für nicht-endgültige Mitglieder arbeiten lassen? –

+1

@VenkatSudheerReddyAedama Wahrscheinlich eine neue Frage wert - wenn du eine postest, kann ich versuchen, heute Abend einen Blick darauf zu werfen. –

Verwandte Themen