Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
Merge branch 'release/0.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
codahale committed May 12, 2011
2 parents 31d4891 + 9102d42 commit 3d4a729
Show file tree
Hide file tree
Showing 30 changed files with 1,771 additions and 185 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,13 @@
v0.2.0: May 12 2011
===================

* Now cross-built for Scala 2.9.0.
* Changed to parse the embedded Scala signature in case classes by using an
embedded version of `scalap`. No longer depends on Paranamer.
* Serializing a case class with a `None` field now elides the entire field
instead of serializing the `Option[A]` as `null`.
* Removed explicit flushes to output.

v0.1.8: May 05 2011
===================

Expand Down
5 changes: 2 additions & 3 deletions README.md
Expand Up @@ -10,9 +10,8 @@ brings Scala's ease-of-use to Jackson's features.
Requirements
------------

* Scala 2.8.1
* Scala 2.8.1 or 2.9.0
* Jackson 1.7.6
* Paranamer 2.3


Setting Up Your Project
Expand All @@ -22,7 +21,7 @@ In your [simple-build-tool](http://code.google.com/p/simple-build-tool/) project
file, add Jerkson as a dependency:

val codaRepo = "Coda Hale's Repository" at "http://repo.codahale.com/"
val jerkson = "com.codahale" %% "jerkson" % "0.1.8"
val jerkson = "com.codahale" %% "jerkson" % "0.2.0"


Parsing JSON
Expand Down
4 changes: 2 additions & 2 deletions project/build.properties
Expand Up @@ -3,6 +3,6 @@
project.organization=com.codahale
project.name=jerkson
sbt.version=0.7.6.RC0
project.version=0.1.8
build.scala.versions=2.8.1
project.version=0.2.0
build.scala.versions=2.8.1 2.9.0
project.initialize=false
7 changes: 3 additions & 4 deletions project/build/JerksonProject.scala
Expand Up @@ -25,12 +25,11 @@ class JerksonProject(info: ProjectInfo) extends DefaultProject(info)
val jacksonVersion = "1.7.6"
val jacksonCore = "org.codehaus.jackson" % "jackson-core-asl" % jacksonVersion
val jacksonMapper = "org.codehaus.jackson" % "jackson-mapper-asl" % jacksonVersion
val paranamer = "com.thoughtworks.paranamer" % "paranamer" % "2.3"

/**
* Test Dependencies
*/
val specs = "org.scala-tools.testing" %% "specs" % "1.6.6" % "test"
val simplespec = "com.codahale" %% "simplespec" % "0.2.0" % "test"
val mockito = "org.mockito" % "mockito-all" % "1.8.4" % "test"
val simplespec = "com.codahale" %% "simplespec" % "0.3.2" % "test"
def specs2Framework = new TestFramework("org.specs2.runner.SpecsFramework")
override def testFrameworks = super.testFrameworks ++ Seq(specs2Framework)
}
2 changes: 1 addition & 1 deletion src/main/scala/com/codahale/jerkson/ScalaModule.scala
Expand Up @@ -14,7 +14,7 @@ class ScalaModule extends Module {
def version = new Version(0, 2, 0, "")
def getModuleName = "jerkson"

def setupModule(context: SetupContext) = {
def setupModule(context: SetupContext) {
context.addDeserializers(new ScalaDeserializers)
context.addSerializers(new ScalaSerializers)
}
Expand Down
@@ -1,60 +1,48 @@
package com.codahale.jerkson.deser

import org.codehaus.jackson.`type`.JavaType
import com.thoughtworks.paranamer.{BytecodeReadingParanamer, CachingParanamer}
import org.codehaus.jackson.map.`type`.TypeFactory
import org.codehaus.jackson.node.{NullNode, TreeTraversingParser}
import collection.mutable.ArrayBuffer
import org.codehaus.jackson.map._
import com.codahale.jerkson.Json
import org.codehaus.jackson.{JsonNode, JsonToken, JsonParser}

object CaseClassDeserializer {
val paranamer = new CachingParanamer(new BytecodeReadingParanamer)
}
import com.codahale.jerkson.util._

class CaseClassDeserializer(config: DeserializationConfig,
javaType: JavaType,
provider: DeserializerProvider) extends JsonDeserializer[Object] {
import CaseClassDeserializer._

val constructors = javaType.getRawClass.getConstructors.map { constructor =>
val names = paranamer.lookupParameterNames(constructor).toSeq
val types = constructor.getGenericParameterTypes.toSeq
names.zip(types) -> constructor
}.toSeq

require(javaType.getRawClass.getConstructors.length == 1, "Case classes must only have one constructor.")
private val constructor = javaType.getRawClass.getConstructors.head
private val params = CaseClassSigParser.parse(javaType.getRawClass).toArray

def deserialize(jp: JsonParser, ctxt: DeserializationContext): Object = {
if (jp.getCurrentToken == JsonToken.START_OBJECT) {
jp.nextToken()
}

if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
if (jp.getCurrentToken != JsonToken.FIELD_NAME &&
jp.getCurrentToken != JsonToken.END_OBJECT) {
throw ctxt.mappingException(javaType.getRawClass)
}

val node = jp.readValueAsTree

for ((params, constructor) <- constructors) {
val values = new ArrayBuffer[AnyRef]
for ((name, paramType) <- params) {
val field = node.get(name)
val tp = new TreeTraversingParser(if (field == null) NullNode.getInstance else field, jp.getCodec)
val javaType = TypeFactory.`type`(paramType)
val value = if (javaType.getRawClass == classOf[Option[_]]) {
// thanks again for special-casing VALUE_NULL
Option(tp.getCodec.readValue[Object](tp, javaType.containedType(0)))
} else {
tp.getCodec.readValue[Object](tp, javaType)
}

if (field != null || value != null) {
values += value
}
val values = new ArrayBuffer[AnyRef]
for ((paramName, paramType) <- params) {
val field = node.get(paramName)
val tp = new TreeTraversingParser(if (field == null) NullNode.getInstance else field, jp.getCodec)
val value = if (paramType.getRawClass == classOf[Option[_]]) {
// thanks again for special-casing VALUE_NULL
Option(tp.getCodec.readValue[Object](tp, paramType.containedType(0)))
} else {
tp.getCodec.readValue[Object](tp, paramType)
}

if (field != null || value != null) {
values += value
}


if (values.size == params.size) {
return constructor.newInstance(values.toArray: _*).asInstanceOf[Object]
}
Expand All @@ -64,12 +52,8 @@ class CaseClassDeserializer(config: DeserializationConfig,
}

private def errorMessage(node: JsonNode) = {
val names = constructors.map { case (params, _) => params.map { case (name, _) => name }.mkString("[", ", ", "]") }
val options = names match {
case Seq(name) => name
case l => l.mkString("either ", " or ", "")
}
val names = params.map {_._1}.mkString("[", ", ", "]")
val existing = Json.parse[Map[String, JsonNode]](node).keys.mkString("[", ", ", "]")
"Invalid JSON. Needed %s, but found %s.".format(options, existing)
"Invalid JSON. Needed %s, but found %s.".format(names, existing)
}
}
Expand Up @@ -9,14 +9,14 @@ class IteratorDeserializer(elementType: JavaType,
def deserialize(jp: JsonParser, ctxt: DeserializationContext) = {
val builder = Seq.newBuilder[Object]

if (jp.getCurrentToken() != JsonToken.START_ARRAY) {
if (jp.getCurrentToken != JsonToken.START_ARRAY) {
throw ctxt.mappingException(elementType.getRawClass)
}

while (jp.nextToken() != JsonToken.END_ARRAY) {
builder += elementDeserializer.deserialize(jp, ctxt).asInstanceOf[Object]
}

builder.result.iterator
builder.result().iterator
}
}
Expand Up @@ -20,13 +20,13 @@ class MapDeserializer[CC[A, B] <: Map[A, B] with MapLike[A, B, CC[A, B]]](compan
jp.nextToken()
}

while (jp.getCurrentToken() != JsonToken.END_OBJECT) {
while (jp.getCurrentToken != JsonToken.END_OBJECT) {
val name = jp.getCurrentName
jp.nextToken()
builder += ((name, valueDeserializer.deserialize(jp, ctxt)))
jp.nextToken()
}

builder.result
builder.result()
}
}
Expand Up @@ -15,14 +15,14 @@ class SeqDeserializer[+CC[X] <: Traversable[X]](companion: GenericCompanion[CC],
def deserialize(jp: JsonParser, ctxt: DeserializationContext) = {
val builder = companion.newBuilder[Object]

if (jp.getCurrentToken() != JsonToken.START_ARRAY) {
if (jp.getCurrentToken != JsonToken.START_ARRAY) {
throw ctxt.mappingException(elementType.getRawClass)
}

while (jp.nextToken() != JsonToken.END_ARRAY) {
builder += elementDeserializer.deserialize(jp, ctxt).asInstanceOf[Object]
}

builder.result
builder.result()
}
}
Expand Up @@ -17,15 +17,16 @@ class CaseClassSerializer[A <: Product](klass: Class[_]) extends JsonSerializer[
json.writeStartObject()
for (field <- nonIgnoredFields) {
val methodOpt = methods.get(field.getName)
json.writeFieldName(methodOpt.map { _.getName }.getOrElse(field.getName))
val fieldValue: Object = methodOpt.map { _.invoke(value) }.getOrElse(field.get(value))
if (fieldValue == null) {
provider.getNullValueSerializer.serialize(null, json, provider)
} else {
val serializer = provider.findValueSerializer(fieldValue.getClass)
serializer.serialize(fieldValue, json, provider)
if (fieldValue != None) {
json.writeFieldName(methodOpt.map {_.getName}.getOrElse(field.getName))
if (fieldValue == null) {
provider.getNullValueSerializer.serialize(null, json, provider)
} else {
val serializer = provider.findValueSerializer(fieldValue.getClass, null)
serializer.serialize(fieldValue, json, provider)
}
}
json.flush()
}
json.writeEndObject()
}
Expand Down
Expand Up @@ -8,13 +8,13 @@ import org.codehaus.jackson.map.{SerializerProvider, JsonSerializer}
* @author coda
*/
class EitherSerializer extends JsonSerializer[Either[_, _]] {
def serialize(value: Either[_, _], json: JsonGenerator, provider: SerializerProvider) = {
def serialize(value: Either[_, _], json: JsonGenerator, provider: SerializerProvider) {
val obj: Object = value match {
case Left(o) => o.asInstanceOf[Object]
case Right(o) => o.asInstanceOf[Object]
}

val serializer = provider.findValueSerializer(obj.getClass)
val serializer = provider.findValueSerializer(obj.getClass, null)
serializer.serialize(obj, json, provider)
}
}
Expand Up @@ -8,17 +8,16 @@ import org.codehaus.jackson.map.{SerializerProvider, JsonSerializer}
* @author coda
*/
class IterableSerializer extends JsonSerializer[Iterable[_]] {
def serialize(value: Iterable[_], json: JsonGenerator, provider: SerializerProvider) = {
def serialize(value: Iterable[_], json: JsonGenerator, provider: SerializerProvider) {
json.writeStartArray()
for (element <- value) {
if (element == null) {
provider.getNullValueSerializer.serialize(null, json, provider)
} else {
val obj = element.asInstanceOf[Object]
val serializer = provider.findValueSerializer(obj.getClass)
val serializer = provider.findValueSerializer(obj.getClass, null)
serializer.serialize(obj, json, provider)
}
json.flush()
}
json.writeEndArray()
}
Expand Down
Expand Up @@ -5,17 +5,16 @@ import org.codehaus.jackson.map.{SerializerProvider, JsonSerializer}

class IteratorSerializer extends JsonSerializer[Iterator[_]] {
def serialize(value: Iterator[_], json: JsonGenerator,
provider: SerializerProvider) = {
provider: SerializerProvider) {
json.writeStartArray()
for (element <- value) {
if (element == null) {
provider.getNullValueSerializer.serialize(null, json, provider)
} else {
val obj = element.asInstanceOf[Object]
val serializer = provider.findValueSerializer(obj.getClass)
val serializer = provider.findValueSerializer(obj.getClass, null)
serializer.serialize(obj, json, provider)
}
json.flush()
}
json.writeEndArray()
}
Expand Down
Expand Up @@ -6,9 +6,9 @@ import com.codahale.jerkson.AST._
import java.math.BigInteger

class JValueSerializer extends JsonSerializer[JValue] {
def serialize(value: JValue, json: JsonGenerator, provider: SerializerProvider) = {
def serialize(value: JValue, json: JsonGenerator, provider: SerializerProvider) {
value match {
case JInt(v) => json.writeNumber(new BigInteger(v.toString))
case JInt(v) => json.writeNumber(new BigInteger(v.toString()))
case JFloat(v) => json.writeNumber(v)
case JString(v) => json.writeString(v)
case JBoolean(v) => json.writeBoolean(v)
Expand Down
11 changes: 7 additions & 4 deletions src/main/scala/com/codahale/jerkson/ser/MapSerializer.scala
Expand Up @@ -2,25 +2,28 @@ package com.codahale.jerkson.ser

import org.codehaus.jackson.JsonGenerator
import org.codehaus.jackson.map.{SerializerProvider, JsonSerializer}
import org.codehaus.jackson.map.`type`.TypeFactory

class MapSerializer extends JsonSerializer[Map[_,_]] {
def serialize(value: Map[_, _], json: JsonGenerator, provider: SerializerProvider) = {
def serialize(value: Map[_, _], json: JsonGenerator, provider: SerializerProvider) {
json.writeStartObject()
for ((key, value) <- value) {
if (key == null) {
provider.getNullKeySerializer.serialize(null, json, provider)
} else {
provider.getKeySerializer.serialize(key.asInstanceOf[Object], json, provider)
provider.getKeySerializer(
TypeFactory.`type`(key.asInstanceOf[Object].getClass),
null
).serialize(key.asInstanceOf[Object], json, provider)
}

if (value == null) {
provider.getNullValueSerializer.serialize(null, json, provider)
} else {
val obj = value.asInstanceOf[Object]
val serializer = provider.findValueSerializer(obj.getClass)
val serializer = provider.findValueSerializer(obj.getClass, null)
serializer.serialize(obj, json, provider)
}
json.flush()
}
json.writeEndObject()
}
Expand Down
Expand Up @@ -5,10 +5,10 @@ import org.codehaus.jackson.map.{SerializerProvider, JsonSerializer}

class OptionSerializer extends JsonSerializer[Option[_]] {
def serialize(value: Option[_], json: JsonGenerator,
provider: SerializerProvider) = {
provider: SerializerProvider) {
if (value.isDefined) {
val obj = value.get.asInstanceOf[Object]
val serializer = provider.findValueSerializer(obj.getClass)
val serializer = provider.findValueSerializer(obj.getClass, null)
serializer.serialize(obj, json, provider)
} else {
json.writeNull()
Expand Down

0 comments on commit 3d4a729

Please sign in to comment.