Permalink
Browse files

unpacker!

  • Loading branch information...
1 parent be1de92 commit 3a75c04cf3e5e179bc47f33ac668d2f835ab9059 Robey Pointer committed Aug 22, 2010
@@ -4,4 +4,5 @@ class StreamyJProject(info: ProjectInfo) extends DefaultProject(info)
{
val jackson = "org.codehaus.jackson" % "jackson-core-asl" % "1.5.2"
val specs = "org.scala-tools.testing" % "specs" % "1.6.2.1"
+ val objenesis = "org.objenesis" % "objenesis" % "1.1"
}
@@ -0,0 +1,121 @@
+package com.twitter.streamyj
+
+import java.lang.reflect._
+import scala.reflect.Manifest
+import org.codehaus.jackson.JsonProcessingException
+import org.objenesis.ObjenesisStd
+
+class JsonUnpackingException(reason: String) extends JsonProcessingException(reason)
+
+class StreamyUnpacker {
+ val objenesis = new ObjenesisStd()
+ var ignoreExtraFields = false
+
+ /*
+ def methodsMatching(obj: AnyRef, name: String) = {
+ obj.getClass.getMethods.find { method =>
+ method.getName == name &&
+ method.getReturnType == classOf[Unit] &&
+ method.getParameterTypes.size == 1
+ }.toList
+ }
+*/
+ def setLongField[T](obj: T, field: Field, value: Long) {
+ val t = field.getType
+ if (t == classOf[Int]) {
+ field.setInt(obj, value.toInt)
+ } else if (t == classOf[Long]) {
+ field.setLong(obj, value)
+ } else if (t == classOf[Short]) {
+ field.setShort(obj, value.toShort)
+ } else if (t == classOf[Char]) {
+ field.setChar(obj, value.toChar)
+ } else if (t == classOf[Byte]) {
+ field.setByte(obj, value.toByte)
+ } else {
+ throw new JsonUnpackingException("Missing field conversion: " + field.getName + " of type " +
+ field.getType.toString + " missing conversion from long")
+ }
+ }
+
+ def setField[T](obj: T, field: Field, streamy: Streamy) {
+ streamy.next() match {
+ case ValueLong(x) =>
+ setLongField(obj, field, x)
+
+/*
+ case object StartArray extends StreamyToken
+ case object EndArray extends StreamyToken
+ case object StartObject extends StreamyToken
+ case object EndObject extends StreamyToken
+ case class FieldName(name: String) extends StreamyToken
+ case object NotAvailable extends StreamyToken
+ case object ValueFalse extends StreamyToken
+ case object ValueTrue extends StreamyToken
+ case object ValueNull extends StreamyToken
+ case class ValueDouble(override val value: Double) extends ValueScalar(value)
+ case class ValueString(override val value: String) extends ValueScalar(value)
+*/
+
+// case x: BigDecimal =>
+// case x: Boolean =>
+// case x: String =>
+ // null
+ // array, object
+ }
+ }
+
+ @throws(classOf[JsonProcessingException])
+ def unpackObject[T](streamy: Streamy, cls: Class[T]): T = {
+ val (obj, fields) = makeObject(cls)
+
+ streamy.obj {
+ case FieldName(name) =>
+ fields.find { _.getName == name } match {
+ case None =>
+ if (!ignoreExtraFields) {
+ throw new JsonUnpackingException("Extra field in json object for " + cls + ": " + name)
+ }
+ case Some(field) =>
+ setField(obj, field, streamy)
+ }
+ }
+
+ /*
+ fields.foreach { field =>
+ json.get(field.getName) match {
+ case None =>
+ throw new JsonException("Missing field: " + field.getName)
+ case Some(value) =>
+ setField(obj, field, value)
+ }
+ }
+
+ if (!ignoreExtraFields) {
+ val extraFields = json.keys -- fields.map { _.getName }
+ if (extraFields.size > 0) {
+ throw new JsonException("Extra fields in json: " + extraFields.mkString(", "))
+ }
+ }
+ */
+
+ obj
+ }
+
+ /**
+ * So evil. Make an object without calling its constructor. Then find all
+ * the declared fields and make them accessible. This opens up a case class
+ * for naughtiness.
+ */
+ def makeObject[T](cls: Class[T]): (T, List[Field]) = {
+ val obj = objenesis.newInstance(cls).asInstanceOf[T]
+ val fields = cls.getDeclaredFields().filter { field => !(field.getName contains '$') }.toList
+ fields.foreach { _.setAccessible(true) }
+ (obj, fields)
+ }
+}
+
+object StreamyUnpacker {
+ def apply[T](s: String)(implicit manifest: Manifest[T]) =
+ new StreamyUnpacker().unpackObject(new Streamy(s), manifest.erasure)
+}
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2010 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License. You may obtain
+ * a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.twitter.streamyj
+
+import org.specs._
+import scala.collection.immutable
+
+
+object StreamyUnpackerSpec extends Specification {
+ "StreamyUnpacker" should {
+ "object of ints" in {
+ case class Point(x: Int, y: Int)
+ StreamyUnpacker[Point]("""{"x":50,"y":25}""") mustEqual Point(50, 25)
+
+ }
+ }
+}

0 comments on commit 3a75c04

Please sign in to comment.