Typesafe配置:如何迭代配置项

时间:2013-12-14 13:04:25

标签: scala playframework typesafe-config

在我的Play应用程序中,我有这样的配置:

social {
    twitter {
        url="https://twitter.com"
        logo="images/twitter.png"
    }
    facebook {
        url="https://www.facebook.com"
        logo="images/facebook.png"
    }
}

我是否要遍历所有social条目以获得每个条目的urllogo

<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无效......它只是展示了我想要实现的目标。

最终目标是能够添加任何进一步的社交网址,而无需修改页面模板。

6 个答案:

答案 0 :(得分:11)

如果您将配置更改为:

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

答案 1 :(得分:5)

对于后人来说,这是另一种迭代嵌套配置的方式。我更喜欢这种格式的数组,而不是让我的配置比代码更清晰。

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模板。这就像我能得到它一样干净。

答案 2 :(得分:4)

如果你使用的是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"));
}

答案 3 :(得分:1)

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的情况下完成,只需发布​​一个答案来帮助人们,如果他们想要的话。

答案 4 :(得分:1)

自从 collection.JavaConversions 已被弃用,取而代之的是 collection.JavaConverters(这个问题是搜索如何在 Scala 中迭代类型安全配置的最佳结果),我想建议一个更现代的 Cole 版本很好的答案:

import collection.JavaConverters._

val socialConfig = ConfigFactory.load.getConfig("social")

for ( (name: String, configObject: ConfigObject) <- socialConfig.root.asScala) {
    println(name) // prints "twitter" or "facebook"

    val config = configObject.toConfig
    println(config.getString("url"))
    println(config.getString("logo"))
}

需要明确的是,socialConfig.root.asScala 生成一个标准的 Scala Map[String, ConfigObject],您可以随意迭代它。

答案 5 :(得分:0)

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