scala 类型安全配置:如何迭代配置项

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20583675/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 05:56:06  来源:igfitidea点击:

Typesafe config: How to iterate over configuration items

scalaplayframeworktypesafe-config

提问by j3d

In my Play application I've a configuration like this:

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

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

Ho do I iterate over all the socialentries to get urland logofor each entry?

何做我迭代所有的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>

Of course, item.getStringin the snippet here above does not work... it just shows what I'm trying to achieve.

当然,item.getString在上面的代码片段中不起作用......它只是显示了我想要实现的目标。

The final objective would be to be able to add any further social url without having to modify the page template.

最终目标是能够在无需修改页面模板的情况下添加任何进一步的社交 url。

回答by Pawe? Kozikowski

If you change the config to:

如果将配置更改为:

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

You could do it like this:

你可以这样做:

@(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>

回答by Cole Stanfield

For posterity, here's another way to iterate over a nested config like you had. I prefer that format to the array one and I'd rather make my config cleaner than the code.

对于后代,这是另一种迭代嵌套配置的方法。我更喜欢这种格式而不是数组,我宁愿让我的配置比代码更干净。

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

I'm sure the OP could convert this into a Twirl template. That's about as clean as I can get it.

我确定 OP 可以将其转换为 Twirl 模板。这是我能得到的最干净的。

回答by Haiyuan Zhang

In case you you're using java,this might be a solution :

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

回答by Prashanth Acharya

socialConfig.root.map does not work.

socialConfig.root.map 不起作用。

Here is my solution -

这是我的解决方案 -

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

Where extractSubConfig is a method which produces an ERROR / Config (a scalaz disjunction) and traverseErrorOr is a method to traverse a list and either process all the elements or fail and return failed disjunction if failed at any point. This method can be done without scalaz, just posting an answer to help people if they want.

其中,extractSubConfig 是一种产生 ERROR / Config(scalaz 析取)的方法,而 traverseErrorOr 是一种遍历列表的方法,要么处理所有元素,要么失败并在任何时候失败时返回失败的析取。这种方法可以在没有 scalaz 的情况下完成,只需发布​​答案以帮助人们(如果他们愿意)。

回答by Rubber Duck

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