【问题标题】:Typesafe config: How to iterate over configuration itemsTypesafe config:如何迭代配置项
【发布时间】:2014-01-02 05:39:10
【问题描述】:

在我的 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>

当然,上面 sn-p 中的item.getString 不起作用......它只是显示了我想要实现的目标。

最终目标是能够在无需修改页面模板的情况下添加更多社交网址。

【问题讨论】:

    标签: scala playframework typesafe-config


    【解决方案1】:

    如果您将配置更改为:

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

    【讨论】:

    • Tx,这正是我要找的 :-)
    • 您的某些条目似乎用逗号分隔,而有些则没有。错字?
    【解决方案2】:

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

    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 模板。这是我能做到的最干净的了。

    【讨论】:

      【解决方案3】:

      如果您使用的是 Java,则配置如下:

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

      这将起作用:

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

      【讨论】:

        【解决方案4】:

        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 的情况下完成,只需发布​​一个答案以帮助人们(如果他们愿意)。

        【讨论】:

        • 你也不必使用白名单..这是我的业务需要
        【解决方案5】:

        由于 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],您可以根据需要对其进行迭代。

        【讨论】:

          【解决方案6】:
          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"))
            })
          

          【讨论】:

            【解决方案7】:

            对于最初提到的配置:

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

            Java 中的一个解决方案是:

            val socialConfig = ConfigFactory.load.getConfig("social");
            ConfigList socials = socialConfig.getList("social");
            
            Map<String, Object> map = socials.root().unwrapped();
            for (Map.Entry<String, Object> cv : map.entrySet()) {
              Map<String, String> c = (Map<String, String>)cv.getValue();
              System.out.println(cv.getKey());
              System.out.println(c.getOrDefault("url", ""));
              System.out.println(c.getOrDefault("logo", ""));
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-01-16
              • 2015-02-26
              • 2017-07-20
              • 1970-01-01
              • 1970-01-01
              • 2021-05-14
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多