Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hocon: Read an array of objects from a configuration file

I have created an Play application (2.1) which uses the configuration in conf/application.conf in the Hocon format.

I want to add an array of projects in the configuration. The file conf/application.conf looks like this:

...
projects = [
  {name: "SO", url: "http://stackoverflow.com/"},
  {name: "google", url: "http://google.com"}
]

I try to read this configuration in my Scala project:

import scala.collection.JavaConversions._
case class Project(name: String, url: String)

val projectList: List[Project] =
  Play.maybeApplication.map{x =>
    val simpleConfig = x.configration.getObjectList("projects").map{y =>
      y.toList.map{z =>
        Project(z.get("name").toString, z.get("url").toString) // ?!? doesn't work

      ... 
   }}}}}}}}  // *arg*

This approach seems to be very complicated, I am lost in a lot of Options, and my Eclipse IDE cannot give me any hints about the classes.

Has anybody an example how you can read an array of objects from a Hocon configuration file? Or should I use for this a JSON-file with an JSON-parser instead of Hocon?

like image 961
Sonson123 Avatar asked Jun 19 '13 16:06

Sonson123


People also ask

What is Hocon file?

HOCON (Human-Optimized Config Object Notation) is an easy-to-use configuration format. It is used by Sponge and individual plugins utilizing SpongeAPI to store important data, such as configuration or player data. HOCON files typically use the suffix .

How do you comment Hocon files?

Comment Characters As in standard JSON and standard HOCON, any text between // or # and the next newline is considered a comment and is ignored (unless the // or # is inside a quoted string).

Which characters are used to create a multi line string in Hocon?

Multi-line strings If the three-character sequence """ appears, then all Unicode characters until a closing """ sequence are used unmodified to create a string value. Newlines and whitespace receive no special treatment.

How do I use Typesafe config?

Creating a Typesafe Config With Fallbacks In this case, we first load our overrides. conf , then set a fallback of our defaults. conf from above. When searching for any property, it will first search the original config, then traverse down the fallbacks until a value is found.


4 Answers

The following works for me in Play 2.1.2 (I don't have a .maybeApplication on my play.Play object though, and I'm not sure why you do):

import play.Play
import scala.collection.JavaConversions._
case class Project(name: String, url: String)

val projectList: List[Project] = {
  val projs = Play.application.configuration.getConfigList("projects") map { p => 
    Project(p.getString("name"), p.getString("url")) }
  projs.toList
}

println(projectList)

Giving output:

List(Project(SO,http://stackoverflow.com/), Project(google,http://google.com))

There's not a whole lot different, although I don't get lost in a whole lot of Option instances either (again, different from the API you seem to have).

More importantly, getConfigList seems to be a closer match for what you want to do, since it returns List[play.Configuration], which enables you to specify types on retrieval instead of resorting to casts or .toString() calls.

like image 187
Andy MacKinlay Avatar answered Oct 16 '22 18:10

Andy MacKinlay


What are you trying to accomplish with this part y.toList.map{z =>? If you want a collection of Project as the result, why not just do:

val simpleConfig = x.configration.getObjectList("projects").map{y =>
   Project(y.get("name").toString, y.get("url").toString)
}

In this case, the map operation should be taking instances of ConfigObject which is what y is. That seems to be all you need to get your Project instances, so I'm not sure why you are toListing that ConfigObject (which is a Map) into a List of Tuple2 and then further mapping that again.

like image 41
cmbaxter Avatar answered Oct 16 '22 19:10

cmbaxter


If a normal HOCON configuration then similar to strangefeatures answer this will work

import javax.inject._
import play.api.Configuration

trait Barfoo {
  def configuration: Configuration     
  def projects = for {
    projectsFound <- configuration.getConfigList("projects").toList
    projectConfig <- projectsFound
    name <- projectConfig.getString("name").toList
    url  <- projectConfig.getString("url").toList
  } yield Project(name,url)
}

class Foobar @Inject() (val configuration: Configuration) extends Barfoo

(Using Play 2.4+ Injection)

like image 2
flurdy Avatar answered Oct 16 '22 20:10

flurdy


Given that the contents of the array are Json and you have a case class, you could try to use the Json Play API and work with the objects in that way. The Inception part should make it trivial.

like image 1
Pere Villega Avatar answered Oct 16 '22 20:10

Pere Villega