diff --git a/README.markdown b/README.markdown index cefa0dd7..1b29552d 100644 --- a/README.markdown +++ b/README.markdown @@ -20,8 +20,7 @@ as depicted in this diagram: ### Installation _spray-json_ is available from maven central. - -Latest release: [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.spray/spray-json_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.spray/spray-json_2.12) +The latest release is `1.3.3` and is built against Scala 2.10.x, 2.11.x, and 2.12.x. If you use SBT you can include _spray-json_ in your project with @@ -42,25 +41,26 @@ import DefaultJsonProtocol._ // if you don't supply your own Protocol (see below and do one or more of the following: * Parse a JSON string into its Abstract Syntax Tree (AST) representation - + ```scala val source = """{ "some": "JSON source" }""" val jsonAst = source.parseJson // or JsonParser(source) ``` - + * Print a JSON AST back to a String using either the `CompactPrinter` or the `PrettyPrinter` - + ```scala val json = jsonAst.prettyPrint // or .compactPrint ``` -* Convert any Scala object to a JSON AST using the `toJson` extension method +* Convert any Scala object to a JSON AST using the pimped `toJson` method + ```scala val jsonAst = List(1, 2, 3).toJson ``` - + * Convert a JSON AST to a Scala object with the `convertTo` method - + ```scala val myObject = jsonAst.convertTo[MyObjectType] ``` @@ -243,38 +243,6 @@ object MyJsonProtocol extends DefaultJsonProtocol { This is a bit more verbose in its definition and the resulting JSON but transports the field semantics over to the JSON side. Note that this is the approach _spray-json_ uses for case classes. -### Providing JsonFormats for unboxed types - -A value class - -```scala -case class PhoneNumber(value: String) extends AnyVal -val num = PhoneNumber("+1 212 555 1111") -``` - -or a class with multiple members - -```scala -case class Money(currency: String, amount: BigDecimal) -val bal = Money("USD", 100) -``` - -can be handled as above with `jsonFormatX`, etc. -It may be preferable, however, to serialize such instances without object boxing: -as `"USD 100"` instead of `{"currency":"USD","amount":100}`. -This requires explicit (de)serialization logic: - -```scala -implicit object MoneyFormat extends JsonFormat[Money] { - val fmt = """([A-Z]{3}) ([0-9.]+)""".r - def write(m: Money) = JsString(s"${m.currency} ${m.amount}") - def read(json: JsValue) = json match { - case JsString(fmt(c, a)) => Money(c, BigDecimal(a)) - case _ => deserializationError("String expected") - } -} -``` - ### JsonFormat vs. RootJsonFormat @@ -329,22 +297,20 @@ _spray-json_ is licensed under [APL 2.0]. ### Mailing list -Spray-json is in primarily "maintanance mode", as it contains the basic functionality it is meant to deliver. -If you have any questions about it though, please open issues on this repository. - +Please use the [spray-user] mailing list if you have any questions. -### Maintanance mode -_spray-json_ is largely considered feature-complete for the basic functionality it provides. -It is currently maintained by the Akka team at Lightbend. +### Patch Policy Feedback and contributions to the project, no matter what kind, are always very welcome. - +However, patches can only be accepted from their original author. Along with any patches, please state that the patch is your original work and that you license the work to the _spray-json_ project under the project’s open source license. [JSON]: http://json.org + [repo.spray.io]: http://repo.spray.io [SJSON]: https://github.com/debasishg/sjson - [Databinder-Dispatch]: https://github.com/dispatch/classic + [Databinder-Dispatch]: https://github.com/n8han/Databinder-Dispatch [APL 2.0]: http://www.apache.org/licenses/LICENSE-2.0 + [spray-user]: http://groups.google.com/group/spray-user diff --git a/src/main/scala/spray/json/JsValue.scala b/src/main/scala/spray/json/JsValue.scala index 1de810e4..ca4cf6eb 100644 --- a/src/main/scala/spray/json/JsValue.scala +++ b/src/main/scala/spray/json/JsValue.scala @@ -1,7 +1,7 @@ /* * Copyright (C) 2009-2011 Mathias Doenitz * Inspired by a similar implementation by Nathan Hamblen - * (https://github.com/dispatch/classic) + * (https://github.com/n8han/Databinder-Dispatch) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,6 @@ sealed abstract class JsValue { def toString(printer: (JsValue => String)) = printer(this) def compactPrint = CompactPrinter(this) def prettyPrint = PrettyPrinter(this) - def sortedPrint = SortedPrinter(this) def convertTo[T :JsonReader]: T = jsonReader[T].read(this) /** @@ -124,5 +123,5 @@ case object JsFalse extends JsBoolean { */ case object JsNull extends JsValue -/** The representation for JSON missing value. **/ +/** The representation for JSON undefined. **/ case object JsUndefined extends JsValue diff --git a/src/main/scala/spray/json/JsonParser.scala b/src/main/scala/spray/json/JsonParser.scala index 71c4c119..a279ad51 100644 --- a/src/main/scala/spray/json/JsonParser.scala +++ b/src/main/scala/spray/json/JsonParser.scala @@ -60,6 +60,7 @@ class JsonParser(input: ParserInput) { (cursorChar: @switch) match { case 'f' => simpleValue(`false`(), JsFalse) case 'n' => simpleValue(`null`(), JsNull) + case 'u' => simpleValue(`undefined`(), JsUndefined) case 't' => simpleValue(`true`(), JsTrue) case '{' => advance(); `object`() case '[' => advance(); `array`() @@ -71,6 +72,7 @@ class JsonParser(input: ParserInput) { private def `false`() = advance() && ch('a') && ch('l') && ch('s') && ws('e') private def `null`() = advance() && ch('u') && ch('l') && ws('l') + private def `undefined`() = advance() && ch('n') && ch('d') && ch('e') && ch('f') && ch('i') && ch('n') && ch('e') && ws('d') private def `true`() = advance() && ch('r') && ch('u') && ws('e') // http://tools.ietf.org/html/rfc4627#section-2.2 diff --git a/src/main/scala/spray/json/JsonPrinter.scala b/src/main/scala/spray/json/JsonPrinter.scala index 8c62cf97..70a23d26 100644 --- a/src/main/scala/spray/json/JsonPrinter.scala +++ b/src/main/scala/spray/json/JsonPrinter.scala @@ -44,6 +44,7 @@ trait JsonPrinter extends (JsValue => String) { protected def printLeaf(x: JsValue, sb: JStringBuilder) { x match { case JsNull => sb.append("null") + case JsUndefined => sb.append("undefined") case JsTrue => sb.append("true") case JsFalse => sb.append("false") case JsNumber(x) => sb.append(x) diff --git a/src/test/scala/spray/json/CompactPrinterSpec.scala b/src/test/scala/spray/json/CompactPrinterSpec.scala index b804d2ef..d8f4acdc 100644 --- a/src/test/scala/spray/json/CompactPrinterSpec.scala +++ b/src/test/scala/spray/json/CompactPrinterSpec.scala @@ -81,8 +81,8 @@ class CompactPrinterSpec extends Specification { mustEqual "{}" ) "properly print a simple JsArray" in ( - CompactPrinter(JsArray(JsNull, JsNumber(1.23), JsObject("key" -> JsBoolean(true)))) - mustEqual """[null,1.23,{"key":true}]""" + CompactPrinter(JsArray(JsNull, JsUndefined, JsNumber(1.23), JsObject("key" -> JsBoolean(true)))) + mustEqual """[null,undefined,1.23,{"key":true}]""" ) "properly print a JSON padding (JSONP) if requested" in { CompactPrinter(JsTrue, Some("customCallback")) mustEqual("customCallback(true)") diff --git a/src/test/scala/spray/json/JsonParserSpec.scala b/src/test/scala/spray/json/JsonParserSpec.scala index a97f0214..b4745b5f 100644 --- a/src/test/scala/spray/json/JsonParserSpec.scala +++ b/src/test/scala/spray/json/JsonParserSpec.scala @@ -24,6 +24,9 @@ class JsonParserSpec extends Specification { "parse 'null' to JsNull" in { JsonParser("null") === JsNull } + "parse 'undefined' to JsUndefined" in { + JsonParser("undefined") === JsUndefined + } "parse 'true' to JsTrue" in { JsonParser("true") === JsTrue } @@ -57,8 +60,8 @@ class JsonParserSpec extends Specification { JsObject("key" -> JsNumber(42), "key2" -> JsString("value")) ) "parse a simple JsArray" in ( - JsonParser("""[null, 1.23 ,{"key":true } ] """) === - JsArray(JsNull, JsNumber(1.23), JsObject("key" -> JsTrue)) + JsonParser("""[null, undefined, 1.23 ,{"key":true } ] """) === + JsArray(JsNull, JsUndefined, JsNumber(1.23), JsObject("key" -> JsTrue)) ) "parse directly from UTF-8 encoded bytes" in { val json = JsObject( diff --git a/src/test/scala/spray/json/ProductFormatsSpec.scala b/src/test/scala/spray/json/ProductFormatsSpec.scala index be3a13e3..f4086530 100644 --- a/src/test/scala/spray/json/ProductFormatsSpec.scala +++ b/src/test/scala/spray/json/ProductFormatsSpec.scala @@ -60,7 +60,7 @@ class ProductFormatsSpec extends Specification { JsObject("b" -> JsNumber(4.2)).convertTo[Test2] must throwA(new DeserializationException("Object is missing required member 'a'")) ) - "not require the presence of Option fields for deserialization" in { + "not require the presence of optional fields for deserialization" in { JsObject("a" -> JsNumber(42)).convertTo[Test2] mustEqual Test2(42, None) } "not require the presence of Tription fields for deserialization" in {