Sunday 15 April 2012

json - Scala - Instantiate classes dynamically from a String -



json - Scala - Instantiate classes dynamically from a String -

i trying create dynamic parser allows me parse json content different classes depending on class name.

i json , class name (as string) , this:

val thecaseclassname = "com.ardlema.jdbcdataproviderproperties" val mycaseclass = class.forname(thecaseclassname) val jsonjdbcproperties = """{"url":"myurl","username":"theuser","password":"thepassword"}""" val json = json.parse(jsonjdbcproperties) val value = try(json.as[myclass])

the above code not compile because json.as[] method tries convert node "t" (i have implicit reads[t] defined case class)

what best way proper "t" pass in json.as[] method original string?

a great solution might work polymorphic deserialization. allows add together field (like "type") json , allow jackson (assuming you're using awesome json parser jackson) figure out proper type on behalf. looks might not using jackson; promise it's worth using.

this post gives great introduction polymorphic types. covers many useful cases including case can't modify 3rd party code (here add together mixin annotate type hierarchy).

the simplest case ends looking (and of works great scala objects -- jackson has great scala module):

object test { @jsontypeinfo( utilize = jsontypeinfo.id.name, include = jsontypeinfo.as.property, property = "type" ) @jsonsubtypes(array( new type(value = classof[cat], name = "cat"), new type(value = classof[dog], name = "dog") )) trait animal case class dog(name: string, breed: string, leash_color: string) extends animal case class cat(name: string, favorite_toy: string) extends animal def main(args: array[string]): unit = { val objectmapper = new objectmapper scalaobjectmapper objectmapper.registermodule(defaultscalamodule) val dogstr = """{"type": "dog", "name": "spike", "breed": "mutt", "leash_color": "red"}""" val catstr = """{"type": "cat", "name": "fluffy", "favorite_toy": "spider ring"}""" val animal1 = objectmapper.readvalue[animal](dogstr) val animal2 = objectmapper.readvalue[animal](catstr) println(animal1) println(animal2) } }

this generates output:

// dog(spike,mutt,red) // cat(fluffy,spider ring)

you can avoid listing subtype mapping, requires json "type" field bit more complex. experiment it; might it. define animal this:

@jsontypeinfo( utilize = jsontypeinfo.id.class, include = jsontypeinfo.as.property, property = "type" ) trait animal

and produces (and consumes) json this:

/* { "breed": "mutt", "leash_color": "red", "name": "spike", "type": "classpath.to.test$dog" } { "favorite_toy": "spider ring", "name": "fluffy", "type": "classpath.to.test$cat" } */

json scala reflection

No comments:

Post a Comment