1

I am trying out scala.js, and would like to use simple extracted row data in json format from my Postgres database.

Here is an contrived example of the type of json I would like to parse into some strongly typed scala collection, features to note are that there are n rows, various column types including an array just to cover likely real life scenarios, don't worry about the SQL which creates an inline table to extract the JSON from, I've included it for completeness, its the parsing of the JSON in scala.js that is causing me problems

with inline_t as (  
select * from (values('One',1,True,ARRAY[1],1.0),
                     ('Six',6,False,ARRAY[1,2,3,6],2.4494),
                     ('Eight',8,False,ARRAY[1,2,4,8],2.8284)) as v (as_str,as_int,is_odd,factors,sroot))
select json_agg(t) from inline_t t;

 [{"as_str":"One","as_int":1,"is_odd":true,"factors":[1],"sroot":1.0}, 
 {"as_str":"Six","as_int":6,"is_odd":false,"factors":[1,2,3,6],"sroot":2.4494}, 
 {"as_str":"Eight","as_int":8,"is_odd":false,"factors":[1,2,4,8],"sroot":2.8284}]

I think this should be fairly easy using something like upickle or prickle as hinted at here: How to parse a json string to a case class in scaja.js and vice versa but I haven't been able to find a code example, and I'm not up to speed enough with Scala or Scala.js to work it out myself. I'd be very grateful if someone could post some working code to show how to achieve the above

EDIT This is the sort of thing I've tried, but I'm not getting very far

val jsparsed = scala.scalajs.js.JSON.parse(jsonStr3)

val jsp1 = jsparsed.selectDynamic("1")

val items = jsp1.map{ (item: js.Dynamic) =>
  (item.as_str, item.as_int, item.is_odd, item.factors, item.sroot)
  .asInstanceOf[js.Array[(String, Int, Boolean, Array[Int], Double)]].toSeq
}
println(items._1)
Community
  • 1
  • 1
  • Parsing itself can be done trivially with `scala.scalajs.js.JSON.parse(jsonString)`. Further than that, it is unclear what is the representation you want to receive in return. Is it a data structure with case classes? – sjrd Feb 12 '16 at 13:22
  • Thanks sjrd, I tried the parsing as you suggested before I posted, but I cannot find the right syntax to unmarshall the parsed object into scala variables. I would like to create an array of tuples, but I don't know javascript well (hence I am interested in scala.js). I can't make it work - if someone can show me something that works, I can elaborate/explore/understand it, but I am struggling to get to something that works! – Simon Young Feb 13 '16 at 08:57
  • val jsparsed = scala.scalajs.js.JSON.parse(jsonStr3) val jsp1 = jsparsed.selectDynamic("1") val items = jsp1.map{ (item: js.Dynamic) => (item.as_str, item.as_int, item.is_odd, item.factors, item.sroot) .asInstanceOf[js.Array[(String, Int, Boolean, Array[Int], Double)]].toSeq } println(items._1) – Simon Young Feb 13 '16 at 09:01
  • Apologies for messy comments, I posted code to original question now. Epic fail on using Stack Overflow correctly too it seems. – Simon Young Feb 13 '16 at 09:08

1 Answers1

2

So you are in a situation where you actually want to manipulate JSON values. Since you're not serializing/deserializing Scala values from end-to-end, serialization libraries like uPickle or Prickle will not be very helpful to you.

You could have a look at a cross-platform JSON manipulation library, such as circe. That would give you the advantage that you wouldn't have to "deal with JavaScript data structures" at all. Instead, the library would parse your JSON and expose it as a Scala data structure. This is probably the best option if you want your code to also cross-compile.

If you're only writing Scala.js code, and you want a more lightweight version (no dependency), I recommend declaring types for your JSON "schema", then use those types to perform the conversion in a safer way:

import scala.scalajs.js
import scala.scalajs.js.annotation._

// type of {"as_str":"Six","as_int":6,"is_odd":false,"factors":[1,2,3,6],"sroot":2.4494}
@ScalaJSDefined
trait Row extends js.Object {
  val as_str: String
  val as_int: Int
  val is_odd: Boolean
  val factors: js.Array[Int]
  val sroot: Double
}

type Rows = js.Array[Row]

val rows = js.JSON.parse(jsonStr3).asInstanceOf[Rows]

val items = (for (row <- rows) yield {
  import row._
  (as_str, as_int, is_odd, factors.toArray, sroot)
}).toSeq
sjrd
  • 21,805
  • 2
  • 61
  • 91