在我的Play应用程序中,我有一个这样的配置:
social {
twitter {
url="https://twitter.com"
logo="images/twitter.png"
}
facebook {
url="https://www.facebook.com"
logo="images/facebook.png"
}
}
何我迭代所有social
条目以获得每个条目的url
和logo
?
<table border="0" cellspacing="0" cellpadding="2"><tr>
@configuration.getConfig("social").map { config =>
@for(item <- config.entrySet) {
<td><a href="item.getString("url")">
<img src="@routes.Assets.at("item.getString("logo")").absoluteURL()" width="24" height="24"/></a></td>
}
}
</table>
当然,上面代码段中的item.getString
不起作用......它只是展示了我想要实现的目标。
最终目标是能够添加任何进一步的社交URL,而无需修改页面模板。
如果您将配置更改为:
"social" : [
{
name="twitter",
url="https://twitter.com",
logo="images/twitter.png"
},
{
name="facebook",
url="https://www.facebook.com",
logo="images/facebook.png"
}
]
你可以这样做:
@(message: String)(implicit request: RequestHeader)
@import play.api.Play.current
<table border="0" cellspacing="0" cellpadding="2"><tr>
@current.configuration.getConfigList("social").get.map { config =>
<td><a href="@config.getString("url")">
<img src="@routes.Assets.at(config.getString("logo").get).absoluteURL()" width="24" height="24"/></a></td>
}
</table>
对于后代,这是另一种迭代嵌套配置的方法。我更喜欢这种格式的数组,我宁愿让我的配置比代码更干净。
import collection.JavaConversions._
val socialConfig = ConfigFactory.load.getConfig("social")
socialConfig.root.map { case (name: String, configObject: ConfigObject) =>
val config = configObject.toConfig
println(config.getString("url"))
println(config.getString("logo"))
}
我确信OP可以将其转换为Twirl模板。这就像我能得到它一样干净。
如果您使用的是Java,这可能是一个解决方案:
ConfigList socials = ConfigFactory().load.getList("social")
for (ConfigValue cv : socials) {
Config c = ((ConfigObject)cv).toConfig();
System.out.println(c.getString("url"));
System.out.println(c.getString("logo"));
}
socialConfig.root.map不起作用。
这是我的解决方案 -
private val firstSegmentRE = """^(\w+)[\.*].*$""".r
// convert "aaa.bbb.ccc" to "aaa"
private def parseFirstSegment(fullPath: String) : Option[String] = {
if (fullPath.contains("."))
fullPath match {
case firstSegmentRE(segment) => Some(segment)
case _ => None
}
else
Some(fullPath)
}
// for all keys in white list get a map of key -> config
protected def subConfigMap(config: Config, whiteList: List[String],
configName: String) : ErrorOr[Map[String, Config]] = {
// This will traverse the whole config and flatten down to the leaves..
val leafKeys : List[String] =
config.entrySet()
.asScala
.map(e => e.getKey)
.toList
// Remove all after the first dot
val nextLevelKeys : List[String] =
leafKeys.map(parseFirstSegment)
.collect {
case Some(firstSegment) => firstSegment
}
.distinct
val keysToSearch = nextLevelKeys.filter(whiteList.contains)
// we have a list of valid first level children
// parse out subconfigs and convert to map
keysToSearch.traverseErrorOr( key =>
extractSubConfig(config, key, configName).map((key, _))
)
.map(_.toMap)
}
其中extractSubConfig是一个产生ERROR / Config(一个scalaz析取)的方法,而traverseErrorOr是一个遍历列表并处理所有元素或者失败的方法,如果在任何时候失败则返回失败的析取。这种方法可以在没有scalaz的情况下完成,只需发布一个答案来帮助人们,如果他们想要的话。
import collection.JavaConversions._
val socialConfig = ConfigFactory.load.getConfig("social")
val socialConfigMap = socialConfig
.root()
.entrySet()
.asScala
.map(socialEntry => {
println(socialEntry.getKey)
val socialEntryConfig = socialEntry.getValue.asInstanceOf[ConfigObject].toConfig
println(socialEntryConfig.getString("url"))
println(socialEntryConfig.getString("logo"))
})