Last active
August 29, 2015 14:02
-
-
Save sadache/d357ced8f6c942bca81a to your computer and use it in GitHub Desktop.
Faster with much less memory consumption JSON object reader
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
def objectReader[T1,T2,T3,T4,R](t1: String, t2: String, t3: String, t4: String)(f: (T1,T2,T3,T4) => R)(implicit readsT1:Reads[T1], readsT2:Reads[T2], readsT3:Reads[T3], readsT4:Reads[T4]): Reads[R] = { | |
def orElse[A](a:A, default: =>A) = if(a!=null) a else default | |
Reads[R]{ | |
case JsObject(fields) => | |
var t1V:JsResult[T1] = null.asInstanceOf[JsResult[T1]] | |
var t2V:JsResult[T2] = null.asInstanceOf[JsResult[T2]] | |
var t3V:JsResult[T3] = null.asInstanceOf[JsResult[T3]] | |
var t4V:JsResult[T4] = null.asInstanceOf[JsResult[T4]] | |
var els = fields.toIterator | |
while((els.hasNext) && (t1V == null || t2V == null || t3V == null || t4V == null)){ | |
val (key, value) = els.next() | |
key match { | |
case `t1` => t1V = readsT1.reads(value) | |
case `t2` => t2V = readsT2.reads(value) | |
case `t3` => t3V = readsT3.reads(value) | |
case `t4` => t4V = readsT4.reads(value) | |
case _ => // | |
} | |
} | |
(t1V, t2V, t3V, t4V) match { | |
case (JsSuccess(tt1, _), JsSuccess(tt2, _), JsSuccess(tt3, _), JsSuccess(tt4, _)) => | |
JsSuccess(f(tt1, tt2, tt3, tt4)) | |
case _ => | |
List( | |
orElse(t1V, JsError(Seq((JsPath.\(t1), Seq(ValidationError("error.path.missing")))))), | |
orElse(t2V, JsError(Seq((JsPath.\(t2), Seq(ValidationError("error.path.missing")))))), | |
orElse(t3V, JsError(Seq((JsPath.\(t3), Seq(ValidationError("error.path.missing")))))), | |
orElse(t4V, JsError(Seq((JsPath.\(t4), Seq(ValidationError("error.path.missing")))))) | |
).collect { case e: JsError => e }.reduceLeft(_.++(_)) | |
} | |
case js => JsError(Seq(JsPath() -> Seq(ValidationError("error.expected.jsobject")))) | |
} | |
} |
Faster actually. In both cases you'd have to go through all the fields. After this one, you've got the answer, for the other you've got nothing. This one is actually much faster if a given JsValue is used once, and mostly faster if it is not used a lot of times. Since JSON is a transportation and serialisation format in Scala, it makes this algorithm a better fit.
Good point. I'm not a big fan of the solution though, as you may have expected ;)
A definition of JsObject
along the line of case class JsObject(fields: Map[String, JsValue])
would be much more appropriate in your case.
It used to be so, except you'd loose the original order of fields :)
I know. There's no "good" solution :(
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I suspect your saving memory because JsObject is implemented like:
So as soon as you call
\
, the values in yourSeq
may be copied in a Map (depending on the impl oftoMap
), which would be especially bad for nested objects.If it does copy,
value.get(fieldName)
is probably very time efficient. All in all, you're probably trading time for space.