2

As an exercise and some in-house util i'm building, i'd like to convert a java properties file to json hierarchy

foo.bar=15
foo.lots.dir=/tmp/test
foo.baz.host=localhost
foo.baz.port=333

I've managed to converted to a scala map, here's the code:

import java.util._
import scala.io._
import scala.collection.JavaConverters._
import java.io._

val props : Properties = new Properties();

In a repl, you get

scala> props.asScala
res3: scala.collection.mutable.Map[String,String] = Map(foo.bar -> 15, foo.lots.dir -> /tmp/test, foo.baz.host -> localhost, foo.baz.port -> 333)

Question now becomes, how do I iterate this map and collapse the dot notation to a nested map:

 Map(
     foo -> Map(
         bar -> 15, 
         baz -> Map(
             port -> 333, 
             host -> localhost
         )
     )
 )

perhaps using a scala recursion? ;-)

This will feed into a JSON builder and convert it to JSON format. (Which the code i'll post here as edit, once i figure out how to do the above nested map)

dlite922
  • 1,924
  • 3
  • 24
  • 60

2 Answers2

0

Don't even try a nested map. The thing is, you will get enormously tangled with types, trying to differentiate between objects, arrays, leaf nodes, etc. You will end up creating your own Abstract Syntax Tree (AST).

Just iterate over the existing nodes on your map and create a JSON AST directly. I like the one on json4s a lot. Plus, there is a lot of methods in JSON libraries readily available to make your life easier.

Daniel Langdon
  • 5,899
  • 4
  • 28
  • 48
0

It's appears not so simple task as described :) But using scala + functional approach + recursion - it's possible to implement. Check next code.

 val map = Map("foo.bar" -> "15", "foo.lots.dir" -> "/tmp/test", "foo.baz.host" -> "localhost", "foo.baz.port" -> "333")


  def grouping(m: Map[List[String], Any]) : Map[List[String], Any] = {
    println(m)
    val rr = m.filter(_._1.nonEmpty).groupBy(_._1.head).map { el =>
      val internalMap = el._2.map(eel => eel._1.tail -> eel._2)
      internalMap.size match {
        case 1 => el._1 -> internalMap.head._2
        case other => el._1 -> grouping(internalMap)
      }
    }
    rr.map(el => List(el._1) -> el._2)
  }

  println(
    grouping(map.map(e => e._1.split("\\.").toList -> e._2))
  )

My result was:

Map(
    List(foo) -> Map(
        List(baz) -> Map(
            List(host) -> localhost, 
            List(port) -> 333
            ), 
        List(lots) -> /tmp/test, 
        List(bar) -> 15
    )
)

From scientific point of view it was interesting to implement. But honestly i do not see any real application of this. It's better to create Tree where each node described with case class(name, child or value).

vvg
  • 6,325
  • 19
  • 36