Типобезопасная конфигурация: как перебирать элементы конфигурации

В моем приложении 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 без необходимости изменять шаблон страницы.


person j3d    schedule 14.12.2013    source источник


Ответы (5)


Если вы измените конфигурацию на:

"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>
person Paweł Kozikowski    schedule 14.12.2013
comment
Тс, это именно то, что я искал :-) - person j3d; 15.12.2013
comment
Некоторые из ваших записей разделены запятыми, а некоторые нет. Опечатка? - person kfkhalili; 04.09.2018

Для потомков, вот еще один способ перебрать вложенную конфигурацию, как у вас. Я предпочитаю этот формат массиву и лучше сделаю свою конфигурацию чище, чем код.

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

Я уверен, что ОП сможет преобразовать это в шаблон Twirl. Это настолько чисто, насколько я могу это понять.

person Cole Stanfield    schedule 10.04.2014

Если вы используете 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"));
}
person Haiyuan Zhang    schedule 17.10.2014

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, просто опубликовав ответ, чтобы помочь людям, если они хотят.

person Prashanth Acharya    schedule 13.12.2017
comment
Вам также не нужно использовать белый список .. это было нужно мне по бизнесу - person Prashanth Acharya; 13.12.2017

Поскольку collection.JavaConversions с тех пор устарела в пользу collection.JavaConverters (и этот вопрос является лучшим результатом поиска того, как выполнить итерацию Typesafe Config в Scala), я хотел предложить более современную версию отличного ответа Коула:

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], и вы можете перебирать ее, как хотите.

person Sync    schedule 15.03.2021