Last active
April 4, 2018 07:42
-
-
Save piotrga/5233317 to your computer and use it in GitHub Desktop.
The simplest dynamically typed json parsing with Dynamic in Scala 2.10
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import util.parsing.json.JSON | |
import io.Source | |
import scala.language.dynamics | |
object Example extends App{ | |
val json = """{ | |
"name" : "Adam Slodowy", | |
"active": "true", | |
"roles" : [ "teacher", "admin" ], | |
"lessons" : [ { "id": 1 }, { "id": 2 } ], | |
"security" : { "id" : 123, "login": "adams" } | |
}""" | |
val adam = JsonElement.parse(json).get | |
case class Lesson(teacher: String, id: Int, name: String, active : Boolean = true) | |
val lesson = Lesson(adam.name, adam.lessons.at(1).id, adam.lessons.at(1).name, adam.active) | |
// will create Lesson("Adam Slodowy", 2, "", true ) - see the implicit conversions | |
} | |
trait JsonElement extends Dynamic{ self => | |
def selectDynamic(field: String) : JsonElement = EmptyElement | |
def applyDynamic(field: String)(i: Int) : JsonElement = EmptyElement | |
def toList : List[String] = sys.error(s"$this is not a list.") | |
def asString: String = sys.error(s"$this has no string representation.") | |
def length$ : Int = sys.error(s"$this has no length") | |
} | |
object JsonElement{ | |
def ^(s: String) = { | |
require(!s.isEmpty, "Element is empty") | |
s | |
} | |
implicit def toString(e: JsonElement) : String = e.asString | |
implicit def toBoolean(e: JsonElement) : Boolean = (^(e.asString)).toBoolean | |
implicit def toBigDecimal(e: JsonElement) : BigDecimal = BigDecimal(^(e.asString)) | |
implicit def toDouble(e: JsonElement) : Double = ^(e.asString).toDouble | |
implicit def toFloat(e: JsonElement) : Float = ^(e.asString).toFloat | |
implicit def toByte(e: JsonElement) : Byte = ^(e.asString).stripSuffix(".0").toByte | |
implicit def toShort(e: JsonElement) : Short = ^(e.asString).stripSuffix(".0").toShort | |
implicit def toInt(e: JsonElement) : Int = ^(e.asString).stripSuffix(".0").toInt | |
implicit def toLong(e: JsonElement) : Long = ^(e.asString).stripSuffix(".0").toLong | |
implicit def toList(e: JsonElement) : List[String] = e.toList | |
def parse(json: String) = JSON.parseFull(json) map (JsonElement(_)) | |
def apply(any : Any) : JsonElement = any match { | |
case x : Seq[Any] => new ArrayElement(x) | |
case x : Map[String, Any] => new ComplexElement(x) | |
case x => new PrimitiveElement(x) | |
} | |
} | |
case class PrimitiveElement(x: Any) extends JsonElement{ | |
override def asString = x.toString | |
} | |
case object EmptyElement extends JsonElement{ | |
override def asString = "" | |
override def toList = Nil | |
} | |
case class ArrayElement(private val x: Seq[Any]) extends JsonElement{ | |
private lazy val elements = x.map((JsonElement(_))).toArray | |
override def applyDynamic(field: String)(i: Int) : JsonElement = elements.lift(i).getOrElse(EmptyElement) | |
override def toList : List[String] = elements map (_.asString) toList | |
override def length$ : Int = elements.length | |
} | |
case class ComplexElement(private val fields : Map[String, Any]) extends JsonElement{ | |
override def selectDynamic(field: String) : JsonElement = fields.get(field) map(JsonElement(_)) getOrElse(EmptyElement) | |
} | |
Try this new library dijon - it written purely in less than 100 lines of dependency free Scala. It uses Scala dynamic types e.g.
val age = 9
val rick = json"""{ "name": "rick", "age": $age}"""
assert(rick.age == 9)
rick.age = if (rick.age.as[Double].get > 18) "adult" else "youth"
rick.address = `{}`
rick.address.city = "Seattle"
rick.address.isMain = true
assert(rick == json"""{ "name": "rick", "age": "youth", "address": {"city": "Seattle", "isMain": true}}""")
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks a lot for your nice parser, was really helpful to me!
Just a small hint: In order to parse ArrayElements the type of the method should probably be List[JsonElement]