Typesafe配置:如何迭代配置项

问题描述 投票:8回答:5

在我的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不起作用......它只是展示了我想要实现的目标。

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

scala playframework typesafe-config
5个回答
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>

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


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

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


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"))
  })
© www.soinside.com 2019 - 2024. All rights reserved.