GithubHelp home page GithubHelp logo

spray / spray-json Goto Github PK

View Code? Open in Web Editor NEW
969.0 37.0 190.0 924 KB

A lightweight, clean and simple JSON implementation in Scala

License: Apache License 2.0

Scala 100.00%
json json-parser json-serialization scala

spray-json's Introduction

spray-json is a lightweight, clean and efficient JSON implementation in Scala.

It supports the following features:

  • A simple immutable model of the JSON language elements
  • An efficient JSON parser
  • Choice of either compact or pretty JSON-to-string printing
  • Type-class based (de)serialization of custom objects (no reflection, no intrusion)
  • No external dependencies

spray-json allows you to convert between

  • String JSON documents
  • JSON Abstract Syntax Trees (ASTs) with base type JsValue
  • instances of arbitrary Scala types

as depicted in this diagram:

Spray-JSON conversions

Installation

spray-json is available from maven central.

Latest release: Maven Central

If you use SBT you can include spray-json in your project with

libraryDependencies += "io.spray" %%  "spray-json" % "1.3.6"

Usage

spray-json is really easy to use. Just bring all relevant elements in scope with

import spray.json._
import DefaultJsonProtocol._ // if you don't supply your own Protocol (see below)

and do one or more of the following:

  1. Parse a JSON string into its Abstract Syntax Tree (AST) representation

    val source = """{ "some": "JSON source" }"""
    val jsonAst = source.parseJson // or JsonParser(source)
  2. Print a JSON AST back to a String using either the CompactPrinter or the PrettyPrinter

    val json = jsonAst.prettyPrint // or .compactPrint
  3. Convert any Scala object to a JSON AST using the toJson extension method

    val jsonAst = List(1, 2, 3).toJson
  4. Convert a JSON AST to a Scala object with the convertTo method

    val myObject = jsonAst.convertTo[MyObjectType]

In order to make steps 3 and 4 work for an object of type T you need to bring implicit values in scope that provide JsonFormat[T] instances for T and all types used by T (directly or indirectly). The way you normally do this is via a "JsonProtocol".

JsonProtocol

spray-json uses SJSONs Scala-idiomatic type-class-based approach to connect an existing type T with the logic how to (de)serialize its instances to and from JSON. (In fact spray-json even reuses some of SJSONs code, see the 'Credits' section below).

This approach has the advantage of not requiring any change (or even access) to Ts source code. All (de)serialization logic is attached 'from the outside'. There is no reflection involved, so the resulting conversions are fast. Scalas excellent type inference reduces verbosity and boilerplate to a minimum, while the Scala compiler will make sure at compile time that you provided all required (de)serialization logic.

In spray-jsons terminology a 'JsonProtocol' is nothing but a bunch of implicit values of type JsonFormat[T], whereby each JsonFormat[T] contains the logic of how to convert instance of T to and from JSON. All JsonFormat[T]s of a protocol need to be "mece" (mutually exclusive, collectively exhaustive), i.e. they are not allowed to overlap and together need to span all types required by the application.

This may sound more complicated than it is. spray-json comes with a DefaultJsonProtocol, which already covers all of Scala's value types as well as the most important reference and collection types. As long as your code uses nothing more than these you only need the DefaultJsonProtocol. Here are the types already taken care of by the DefaultJsonProtocol:

  • Byte, Short, Int, Long, Float, Double, Char, Unit, Boolean
  • String, Symbol
  • BigInt, BigDecimal
  • Option, Either, Tuple1 - Tuple7
  • List, Array
  • immutable.{Map, Iterable, Seq, IndexedSeq, LinearSeq, Set, Vector}
  • collection.{Iterable, Seq, IndexedSeq, LinearSeq, Set}
  • JsValue

In most cases however you'll also want to convert types not covered by the DefaultJsonProtocol. In these cases you need to provide JsonFormat[T]s for your custom types. This is not hard at all.

Providing JsonFormats for Case Classes

If your custom type T is a case class then augmenting the DefaultJsonProtocol with a JsonFormat[T] is really easy:

case class Color(name: String, red: Int, green: Int, blue: Int)

object MyJsonProtocol extends DefaultJsonProtocol {
  implicit val colorFormat = jsonFormat4(Color)
}

import MyJsonProtocol._
import spray.json._

val json = Color("CadetBlue", 95, 158, 160).toJson
val color = json.convertTo[Color]

The jsonFormatX methods reduce the boilerplate to a minimum, just pass the right one the companion object of your case class and it will return a ready-to-use JsonFormat for your type (the right one is the one matching the number of arguments to your case class constructor, e.g. if your case class has 13 fields you need to use the jsonFormat13 method). The jsonFormatX methods try to extract the field names of your case class before calling the more general jsonFormat overloads, which let you specify the field name manually. So, if spray-json has trouble determining the field names or if your JSON objects use member names that differ from the case class fields you can also use jsonFormat directly.

There is one additional quirk: If you explicitly declare the companion object for your case class the notation above will stop working. You'll have to explicitly refer to the companion objects apply method to fix this:

case class Color(name: String, red: Int, green: Int, blue: Int)
object Color

object MyJsonProtocol extends DefaultJsonProtocol {
  implicit val colorFormat = jsonFormat4(Color.apply)
}

If your case class is generic in that it takes type parameters itself the jsonFormat methods can also help you. However, there is a little more boilerplate required as you need to add context bounds for all type parameters and explicitly refer to the case classes apply method as in this example:

case class NamedList[A](name: String, items: List[A])

object MyJsonProtocol extends DefaultJsonProtocol {
  implicit def namedListFormat[A :JsonFormat] = jsonFormat2(NamedList.apply[A])
}

NullOptions

The NullOptions trait supplies an alternative rendering mode for optional case class members. Normally optional members that are undefined (None) are not rendered at all. By mixing in this trait into your custom JsonProtocol you can enforce the rendering of undefined members as null. (Note that this only affect JSON writing, spray-json will always read missing optional members as well as null optional members as None.)

Providing JsonFormats for other Types

Of course you can also supply (de)serialization logic for types that aren't case classes. Here is one way to do it:

class Color(val name: String, val red: Int, val green: Int, val blue: Int)

object MyJsonProtocol extends DefaultJsonProtocol {
  implicit object ColorJsonFormat extends RootJsonFormat[Color] {
    def write(c: Color) =
      JsArray(JsString(c.name), JsNumber(c.red), JsNumber(c.green), JsNumber(c.blue))

    def read(value: JsValue) = value match {
      case JsArray(Vector(JsString(name), JsNumber(red), JsNumber(green), JsNumber(blue))) =>
        new Color(name, red.toInt, green.toInt, blue.toInt)
      case _ => deserializationError("Color expected")
    }
  }
}

import MyJsonProtocol._

val json = new Color("CadetBlue", 95, 158, 160).toJson
val color = json.convertTo[Color]

This serializes Color instances as a JSON array, which is compact but does not make the elements semantics explicit. You need to know that the color components are ordered "red, green, blue".

Another way would be to serialize Colors as JSON objects:

object MyJsonProtocol extends DefaultJsonProtocol {
  implicit object ColorJsonFormat extends RootJsonFormat[Color] {
    def write(c: Color) = JsObject(
      "name" -> JsString(c.name),
      "red" -> JsNumber(c.red),
      "green" -> JsNumber(c.green),
      "blue" -> JsNumber(c.blue)
    )
    def read(value: JsValue) = {
      value.asJsObject.getFields("name", "red", "green", "blue") match {
        case Seq(JsString(name), JsNumber(red), JsNumber(green), JsNumber(blue)) =>
          new Color(name, red.toInt, green.toInt, blue.toInt)
        case _ => throw new DeserializationException("Color expected")
      }
    }
  }
}

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

case class PhoneNumber(value: String) extends AnyVal
val num = PhoneNumber("+1 212 555 1111")

or a class with multiple members

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:

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

According to the JSON specification not all of the defined JSON value types are allowed at the root level of a JSON document. A JSON string for example (like "foo") does not constitute a legal JSON document by itself. Only JSON objects or JSON arrays are allowed as JSON document roots.

In order to distinguish, on the type-level, "regular" JsonFormats from the ones producing root-level JSON objects or arrays spray-json defines the RootJsonFormat type, which is nothing but a marker specialization of JsonFormat. Libraries supporting spray-json as a means of document serialization might choose to depend on a RootJsonFormat[T] for a custom type T (rather than a "plain" JsonFormat[T]), so as to not allow the rendering of illegal document roots. E.g., the SprayJsonSupport trait of spray-routing is one notable example of such a case.

All default converters in the DefaultJsonProtocol producing JSON objects or arrays are actually implemented as RootJsonFormat. When "manually" implementing a JsonFormat for a custom type T (rather than relying on case class support) you should think about whether you'd like to use instances of T as JSON document roots and choose between a "plain" JsonFormat and a RootJsonFormat accordingly.

JsonFormats for recursive Types

If your type is recursive such as

case class Foo(i: Int, foo: Foo)

you need to wrap your format constructor with lazyFormat and supply an explicit type annotation:

implicit val fooFormat: JsonFormat[Foo] = lazyFormat(jsonFormat(Foo, "i", "foo"))

Otherwise your code will either not compile (no explicit type annotation) or throw an NPE at runtime (no lazyFormat wrapper). Note, that lazyFormat returns a JsonFormat even if it was given a RootJsonFormat which means it isn't picked up by SprayJsonSupport. To get back a RootJsonFormat just wrap the complete lazyFormat call with another call to rootFormat.

Customizing Parser Settings

The parser can be customized by providing a custom instance of JsonParserSettings to JsonParser.apply or String.parseJson:

val customSettings =
  JsonParserSettings.default
     .withMaxDepth(100)
     .withMaxNumberCharacters(20)
val jsValue = JsonParser(jsonString, customSettings)
// or
val jsValue = jsonString.parseJson(customSettings)

Credits

Most of type-class (de)serialization code is nothing but a polished copy of what Debasish Ghosh made available with his SJSON library. These code parts therefore bear his copyright. Additionally the JSON AST model is heavily inspired by the one contributed by Jorge Ortiz to Databinder-Dispatch.

License

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.

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.

Feedback and contributions to the project, no matter what kind, are always very welcome.

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.

spray-json's People

Contributors

aloiscochard avatar anishathalye avatar chris-martin avatar dcheckoway avatar earldouglas avatar ejc123 avatar eranation avatar fommil avatar fractaloop avatar gkossakowski avatar jdanbrown avatar jlprat avatar jroper avatar jrudolph avatar ktoso avatar magnolia-k avatar marcospereira avatar markvandertol avatar martijnhoekstra avatar mpilquist avatar nikhilpatil avatar pasieronen avatar performantdata avatar raboof avatar samskivert avatar sethtisue avatar shankarshastri avatar sirthias avatar titsuki avatar xuwei-k avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spray-json's Issues

non strict JsonParser mode

it would be great if there were a way to allow sloppy JSON (as used by domain languages such as MongoDB) which doesn't require quotation marks around keys and is also happy with single quotes.

NPE when serializing type that references itself

Perhaps I discovered a bug. I get Null Pointer Exception when I'm trying to get json of an object that has field of type of itself. Example is:

case class TestItem(subitems: Option[List[TestItem]])

object MyJsonProtocol extends DefaultJsonProtocol {
  implicit val testItemFormat: RootJsonFormat[TestItem] = jsonFormat(TestItem, "subitems")
}

import MyJsonProtocol._

object TestNPE {
  def main(args: Array[String]) {

    val subitems = List(TestItem(None))
    val item: TestItem = TestItem(Option(subitems))
    val jsonAst = item.toJson
    val json = jsonAst.prettyPrint
    println(json)
  }
}

And call-stack is this

Exception in thread "main" java.lang.NullPointerException
    at spray.json.PimpedAny.toJson(package.scala:40)
    at spray.json.CollectionFormats$$anon$1$$anonfun$write$1.apply(CollectionFormats.scala:26)
    at spray.json.CollectionFormats$$anon$1$$anonfun$write$1.apply(CollectionFormats.scala:26)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
    at scala.collection.immutable.List.foreach(List.scala:309)
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
    at scala.collection.AbstractTraversable.map(Traversable.scala:105)
    at spray.json.CollectionFormats$$anon$1.write(CollectionFormats.scala:26)
    at spray.json.CollectionFormats$$anon$1.write(CollectionFormats.scala:25)
    at spray.json.PimpedAny.toJson(package.scala:40)
    at spray.json.StandardFormats$OptionFormat.write(StandardFormats.scala:34)
    at spray.json.StandardFormats$OptionFormat.write(StandardFormats.scala:32)
    at spray.json.ProductFormats$class.productElement2Field(ProductFormats.scala:473)
    at spray.json.MyJsonProtocol$.productElement2Field(TestNPE.scala:5)
    at spray.json.ProductFormats$$anon$1.write(ProductFormats.scala:32)
    at spray.json.ProductFormats$$anon$1.write(ProductFormats.scala:30)
    at spray.json.PimpedAny.toJson(package.scala:40)
    at spray.json.TestNPE$.main(TestNPE.scala:18)
    at spray.json.TestNPE.main(TestNPE.scala)

I tried to fix it myself but my knowledge of Scala is not strong enough yet. NPE happens here when it's attempting to convert inner TestItem. Function parameter write at that moment is null.

I don't know why it doesn't use my implicit instead.

Why can't I do fromJson to a List[Any]?

There's probably a perfectly good explanation for this, and I really am just looking to understand.

I can parse JSON documents with mixed types, like so:

scala> val j1 = JsonParser( """[1,2]""" )
j1: cc.spray.json.JsValue = [1,2]

scala> val j2 = JsonParser( """[1,2.0]""" )
j2: cc.spray.json.JsValue = [1,2.0]

scala> val j3 = JsonParser( """[1,"two"]""" )
j3: cc.spray.json.JsValue = [1,"two"]

But creating objects from the AST doesn't work as I would expect:

scala> j1.fromJson[ List[Int] ]
res16: List[Int] = List(1, 2)

scala> j2.fromJson[ List[Float] ]
res17: List[Float] = List(1.0, 2.0)

scala> j3.fromJson[ List[Any] ]
:15: error: Cannot find JsonReader or JsonFormat type class for List[Any]

What I think I want is to be able to take such an AST and convert it to a List[Any], with each value converted to its "obvious" type. So in the above example I would like to get a List containing an Int and a String, i.e.

List(1, "two") // what I was expecting

Same for Maps, etc.

Am I thinking about things the wrong way, wanting to do one thing when there's a better way? Or is there a reason (perhaps implicit resolution) why fromJson[ List[Any] ] couldn't/shouldn't behave as I expect?

Thanks for any help.

cannot convert ListMap to json string

I want to convert a ListMap[String, String] to json string through spray. Since ListMap maintains the insertion order of items, I assumed spray json would also return a json string with the items in order. However it seems spray json does not support this yet. Can anybody let me know if there is any workaround for this?

(I am using spray-json_2.10-1.2.3)

scala> import collection.immutable.ListMap
import collection.immutable.ListMap

scala> import spray.json._
import spray.json._

scala> import DefaultJsonProtocol._
import DefaultJsonProtocol._

scala> val map = ListMap[String, Int]("a" -> 1, "b" -> 2, "c" -> 3)
map: scala.collection.immutable.ListMap[String,Int] = Map(a -> 1, b -> 2, c -> 3)

scala> map.asJson
<console>:16: error: value asJson is not a member of scala.collection.immutable.ListMap[String,Int]
              map.asJson
                  ^

Jackson convertor

It would be fantastic if there were a convertor to go between Spray JSON's JsValue and Jackson's JsonNode. That way, Spray JSON could be more efficiently leveraged in traditional containers (with json2xml a one-way bridge to JAXB). Currently, in order to use Spray JSON in traditional containers requires marshalling to/from String

Support default parameters

I am not sure if it’s possible, but would be great if spray-json supported case classes with some of the parameters having default values. What I mean is if we have

case class Test(a: Int = 3, b: Int)

then

{"b": 4}

should deserialize into

Test(3, 4)

instead of failing.

Feedback: Printing and .fromJson interfaces

Despite how many JSON libraries there are, it is nice to see a Scala library aimed at being straightforward and lightweight.

For what it's worth, here are two suggestions that I personally feel would go to making this even better:

Add printing methods to JSON AST object.

The syntax:

val json = PrettyPrinter(jsonAst)

although common to many JSON libraries and allowing for infinite printing libraries in the future, feels very awkward. Given that pretty printing and compact printing are probably 99% of what people want, why not also offer:

val json = jsonAst.prettyPrint

or

val json = jsonAst.compactPrint

More readable code naming of fromJson

The code:

val myObject = jsonAst.fromJson[MyObjectType]

for someone unfamiliar to this library, reads as though the MyObjectType is some form of JSON rather than the type to be convert to from JSON. How about naming it something like convertTo:

val myObject = jsonAst.convertTo[MyObjectType]

Singleton (no parameter) case classes

I wrote this to support no-arg case classes, is it really needed?

case class SingletonCaseClassFormat[T](instance: T) extends JsonFormat[T] {

  override def write(obj: T) = JsString(instance.getClass.getSimpleName)

  override def read(json: JsValue) = json match {
    case JsString(x) =>
      if(x != instance.getClass.getSimpleName)
        deserializationError("Expected %s, but got %s" format (instance.getClass.getSimpleName, x))
      instance
    case x => deserializationError("Expected JsString, but got " + x)
  }
}

Reuse code from scala.util.parsing.{ast,json}

This is more of a question than a report of any kind: how come Spray-JSON isn't reusing the code in scala.util.parsing?

It would be really cool if Spray used the built-in data structures.

Is there a convertor for going to/from the Spray JSON to Scala JSON formats? Some implicits would be nice here, although obviously less optimal than just using the Scala objects.

Maven version for scala 2.10-RC1

Hi,
i try to download the latest build for scala via maven and this pom.xml, without sucess ..

is it only for sbt ? is there any plan to make a compatible maven version ?

 <repositories>
     <repository>
         <id>spray repo</id>
         <name>Spray Repository</name>
         <url>http://repo.spray.io/</url>
     </repository>
 </repositories>

  <dependency>
      <groupId>io.spray</groupId>
      <artifactId>spray-json_${scala.version}</artifactId>
      <version>1.2.2</version>
  </dependency>

UUID support

Please add something like the following to BasicFormats: UUIDs are so widely used that they must be on par with some of the other types in that file.

trait UuidMarshalling {
  implicit object UuidJsonFormat extends JsonFormat[UUID] {
    def write(x: UUID) = JsString(x toString ())
    def read(value: JsValue) = value match {
      case JsString(x) => UUID.fromString(x)
      case x => deserializationError("Expected UUID as JsString, but got " + x)
    }
  }
}

Documentation: mention DefaultJsonProtocol import, resolver snippet in README

Awesome project!

Just running through the Usage examples in your README, it would be useful to know that you need to import spray.json.DefaultJsonProtocol._ in order to get things like List(1, 2, 3).toJson to work.

It might also be helpful for cut-and-pasters like me if you could include the sbt snippet for adding the Spray resolver: resolvers += "spray" at "http://repo.spray.io/".

val instead of object for BigInt and BigDecimal

I would like to use a custom marshaller for BigInt and BigDecimal, to use JsString instead of JsNumber.

The reason for this is because we are re-using Spray JSON marshallers to serialise to/from MongoDB objects. Despite claims that BSON is a superset of JSON, it doesn't support arbitrary precision numbers. i.e. there is no such thing as BsonNumber – it's all ints or longs, floats or doubles. The workaround is that any arbitrary precision numbers must be serialised as String in MongoDB.

Unfortunately, because the implicit JsonFormats are defined as implicit object, the scala compiler will not let me override them. If they were implicit val, I believe this would be ok.

tuple7Format write wrong

The implicit tuple7Format in StandardFormats is repeating element 6 instead of writing element 7.

Inconsistent behavior when deserializing null

Over the last few months using spray-json I had learned the following (incorrect) rule:

null is allowed iff your type is of the form Option[X]

Today I unlearned this rule when I converted a null into Double.NaN (#8) instead triggering an exception, which is what I was expecting to happen. Trying out a bunch of standard formats, I observe these behaviors when deserializing from null:

JsNull.convertTo[Boolean]          -> error
JsNull.convertTo[Int]              -> error
JsNull.convertTo[Long]             -> error
JsNull.convertTo[Char]             -> error
JsNull.convertTo[String]           -> error
JsNull.convertTo[Seq[Int]]         -> error
JsNull.convertTo[Map[String, Int]] -> error
JsNull.convertTo[(Int, String)]    -> error

JsNull.convertTo[Float]            -> Float.NaN
JsNull.convertTo[Double]           -> Double.NaN
JsNull.convertTo[Unit]             -> ()

JsNull.convertTo[Option[Int]]      -> None

I think the library's behavior would be less surprising and thus easier to use if the rule above did hold. To that end, I propose:

  • Exposing an option for null <-> Float.NaN/Double.Nan, disabled by default
  • Rejecting null -> () for Unit

Thoughts? Are there more cases that I'm missing? Is the issue more subtle than this?

Add JsonFormatter16-22

Currently the JsonFormatter stops at 15 parameters for a case class. I propose that it be increased to 22, which is the global parameter limit that Scala has for all functions.

How to Group Sbt Application.conf settings for different apps - Is this Possible? If so What is the Best Approach?

I have a gateway server which communicates with sms, email, database and asp.net servers that has different versions running on different ports. This gateway then sends various responses to different apps. My problem is in application.conf

Current implementation is as below:

app1config{

Common Settings

Server1Port
Server2Host
Server3Path
Server3Host
Server3Port

Unique Settings

app1Config's Port Number
Server1Path
Server1LoginUser
Server1LoginPassword
emailFromAddress
emailSignature
Server2User
Server2Password
Server2BusinessCode
Server2Setting
Server2Config
}
and this is repeated for

app2congig{

same setup

}

app3config{

same setup

}

app4config{

same setup

}

app5config{

same setup

}
The client's requirement is to combine all the settings into one configuration, so that there aren't about 5 versions of the same config.

Now my concern is that spray's strength lies in these different versions whereby the application.conf file is a switch statement and each app#no.config is a case and the main.scala file does not perform much logic except ConfigFactory.load() and place all the variables where needed.

The question is:

  1. Do I combine all settings to be used on one port, add more variables and have logic constructs in main.scala to accommodate all these new variables? ...and consequently add more logic throughout the main.scala file for each nth app added.
  2. Or is there a way of introducing logic constructs, like for loops, while loops, if else statements, break and continue in the application.conf file?...and then pull these automatically?

By this I mean, say there is a unique setting for each app, say Server2Config can I do this?

Server_Config_For All_Apps {

    #Common Settings
    Server1Port = "some setting"
    Server2Host = "some setting"
    Server3Path = "some setting"
    Server3Host = "some setting"
    Server3Port = "some setting" 

    case "App#1Server2Config" =>
     #Unique Settings
     app1ConfigPort Number = "some setting"
     Server1Path = "some setting"
     Server1LoginUser = "some setting"
     Server1LoginPassword = "some setting"
     emailFromAddress = "some setting"
     emailSignature = "some setting"
     Server2User = "some setting"
     Server2Password = "some setting"
     Server2BusinessCode = "some setting"
     Server2Setting = "some setting"
     Server2Config = "some setting"

    case "Some nth app Server2Config" =>
        #Unique Settings
        app1ConfigPort Number = "some setting"
        Server1Path = "some setting"
        Server1LoginUser = "some setting"
        Server1LoginPassword = "some setting"
        emailFromAddress = "some setting"
        emailSignature = "some setting"
        Server2User = "some setting"
        Server2Password = "some setting"
        Server2BusinessCode = "some setting"
        Server2Setting = "some setting"
        Server2Config = "some setting"

   case "App#2Server2Config" =>
        #Unique Settings
        app1ConfigPort Number = "some setting"
        Server1Path = "some setting"
        Server1LoginUser = "some setting"
        Server1LoginPassword = "some setting"
        emailFromAddress = "some setting"
        emailSignature = "some setting"
        Server2User = "some setting"
        Server2Password = "some setting"
        Server2BusinessCode = "some setting"
        Server2Setting = "some setting"
        Server2Config = "some setting"
       }
 }
  1. I could maintain 5 versions but have them all use the same port number?... On this one I wouldn't know about overhead in terms of network traffic.
  2. Do I still continue using this setup?
  3. Is there a different way altogether?

error: Cannot find JsonWriter or JsonFormat type class for List[Int

Hi there, was trying to follow the example in the README however I'm running into a problem, checkout my REPL session:

$ scala -cp /.ivy2/cache/cc.spray.json/spray-json_2.9.1/jars/spray-json_2.9.1-1.0.1.jar:/.ivy2/cache/org.parboiled/parboiled-scala/jars/parboiled-scala-1.0.2.jar:~/.ivy2/cache/org.parboiled/parboiled-core/jars/parboiled-core-1.0.2.jar
Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_26).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import cc.spray.json._
import cc.spray.json._

scala> List(1,2,3).toJson
:11: error: Cannot find JsonWriter or JsonFormat type class for List[Int]
List(1,2,3).toJson

Any thoughts on what I might be doing wrong? Are there extra steps I need to take? Thanks in advance.

JSON marshalling/unmarshalling with Random.nextString test data failure

I wrote a little app to speed profile Spray JSON for simple case classes – answer = Blindingly Fast!!!

However, when I used Random.nextString to generate test String data the marshalling to/from a String (containing JSON data) failed. I can provide a test case if you want to see this in action, but this might be a simple case of you having an answer straight away.

My workaround was to include only printable characters – but I'm now concerned about non-ASCII UTF8 characters.

Add default protocol support for Seq[Any] and Map[String, Any]

Right now there is no protocol that can convert to/from Seq[Any] and Map[String, Any]. This prevents spray-json from being used in many scenarios, unless you code up your own protocol. Spray-json should provide out of the box protocols for these common generics.

I have code that I would like to contribute back for these protocols.

Lift JsonReader/JsonWriter through derived formats

trait A ...
implicit val format = new JsonReader[A] { def read ... }

v.convertTo[A]       // Ok
v.convertTo[List[A]] // Cannot find JsonReader or JsonFormat type class for List[A]

The problem is that def listFormat[T :JsonFormat]: RootJsonFormat[List[T]] lifts JsonFormats but not JsonReaders or JsonWriters, and this appears to be the pattern throughout the formats for collections, products, etc.

I see the lift functions JsonReader[T] => JsonFormat[T] and JsonWriter[T] => JsonFormat[T], but relying on these puts me in a situation when statically I think I have both a reader and writer, but at runtime I discover that I'm actually missing one. To me, static checking is one of the valuable parts of spray-json, so I'm very hesitant to undermine it.

I propose splitting all of the "lifting formats"

(JsonFormat[T], ...) => JsonFormat[F[T, ...]]

into reader-writer pairs

(JsonReader[T], ...) => JsonReader[F[T, ...]]
(JsonWriter[T], ...) => JsonWriter[F[T, ...]]

by turning things like this

implicit def listFormat[T :JsonFormat] = new RootJsonFormat[List[T]] {
  def write(list: List[T]) = JsArray(list.map(_.toJson))
  def read(value: JsValue) = value match {
    case JsArray(elements) => elements.map(_.convertTo[T])
    case x => deserializationError("Expected List as JsArray, but got " + x)
  }
}

into things like this

implicit def listWriter[T :JsonWriter]: JsonWriter[List[T]] = (list: List[T]) => JsArray(list.map(_.toJson))
implicit def listReader[T :JsonReader]: JsonReader[List[T]] = (value: JsValue) => value match {
  case JsArray(elements) => elements.map(_.convertTo[T])
  case x => deserializationError("Expected List as JsArray, but got " + x)
}
implicit def listFormat[T :JsonFormat] = rootJsonFormat[List[T]](implicitly, implicitly)

I'd be happy to contribute a patch, and I welcome suggestions for simpler ways to achieve this. ;)

Sealed traits

if I have a sealed trait

sealed trait Failure

and if I have a bunch of implementations, all case classes, is there a nice way to support marshalling and unmarshalling? I ended up writing one hell of a hack and it occurs to me you might have an elegant solution (my hack involves switching on the class type and inserting the classname into the JSON, then switching on the class name from the JSON and using hard-coded clasasnames to then switch to get the right unmarshaller)

Cannot convert nested Maps into json

Hi, I'm using spray-json 1.2.2 and get this error:

scala> Map("a" -> Map("b" -> 2), "b" -> Map()).toJson
:14: error: Cannot find JsonWriter or JsonFormat type class for scala.collection.immutable.Map[String,scala.collection.immutable.Map[_ <: String, Int]]
Map("a" -> Map("b" -> 2), "b" -> Map()).toJson
^
Doesn't seem like it should be that hard to fix.

NullPointerException when passing null to JsString

Hi,

This is my first use of Spray-Json so I didn't know its rules to handle null values.

I want to serialize a non-case class (Hibernate entity). The code looks like:

implicit object EntityJsonFormat extends RootJsonFormat[Entity] {
    def write(e: Entity) = {
      JsObject(
        "id" -> JsNumber(e.id),
        "name" -> JsString(e.name),
        "desc" -> JsString(e.optional) // this may be null
      )
    }

And it fails with:

2013-08-25 16:03:33,513 [test-akka.actor.default-dispatcher-2] ERROR [akka.actor.RepointableActorRef] - Error during processing of request HttpRequest(GET,http://localhost:8080/test,List(Host: localhost:8080, Authorization: Basic bWFydGluLmdyaWdvcm92QGp3ZWVrZW5kLmNvbTphc2Rm, User-Agent: curl/7.29.0),EmptyEntity,HTTP/1.1)
java.lang.NullPointerException: null
    at spray.json.JsonPrinter$class.firstToBeEncoded$1(JsonPrinter.scala:60) ~[spray-json_2.10-1.2.5.jar:1.2.5]
    at spray.json.JsonPrinter$class.printString(JsonPrinter.scala:63) ~[spray-json_2.10-1.2.5.jar:1.2.5]
    at spray.json.PrettyPrinter$.printString(PrettyPrinter.scala:68) ~[spray-json_2.10-1.2.5.jar:1.2.5]
    at spray.json.JsonPrinter$class.printLeaf(JsonPrinter.scala:52) ~[spray-json_2.10-1.2.5.jar:1.2.5]
    at spray.json.PrettyPrinter$.printLeaf(PrettyPrinter.scala:68) ~[spray-json_2.10-1.2.5.jar:1.2.5]
    at spray.json.PrettyPrinter$class.spray$json$PrettyPrinter$$print(PrettyPrinter.scala:36) ~[spray-json_2.10-1.2.5.jar:1.2.5]
    at spray.json.PrettyPrinter$$anonfun$printObject$2.apply(PrettyPrinter.scala:46) ~[spray-json_2.10-1.2.5.jar:1.2.5]
    at spray.json.PrettyPrinter$$anonfun$printObject$2.apply(PrettyPrinter.scala:42) ~[spray-json_2.10-1.2.5.jar:1.2.5]
    at spray.json.JsonPrinter$$anonfun$printSeq$1.apply(JsonPrinter.scala:94) ~[spray-json_2.10-1.2.5.jar:1.2.5]
    at spray.json.JsonPrinter$$anonfun$printSeq$1.apply(JsonPrinter.scala:92) ~[spray-json_2.10-1.2.5.jar:1.2.5]
    at scala.collection.Iterator$class.foreach(Iterator.scala:727) ~[scala-library-2.10.2.jar:na]
    at scala.collection.AbstractIterator.foreach(Iterator.scala:1157) ~[scala-library-2.10.2.jar:na]
....

To overcome this NPE I changed my code to:

implicit object EntityJsonFormat extends RootJsonFormat[Entity] {
    def write(e: Entity) = {
      val optJs =
        if (e.optional != null) JsString(e.optional)
        else JsNull
      JsObject(
        "id" -> JsNumber(e.id),
        "name" -> JsString(e.name),
        "desc" -> optJs
      )
    }

I have two suggestions:

  1. add require(arg != null) in JsString#apply(String) (and other JsValue impls)
  2. or degrade JsString(null) automatically to JsNull

In any case the NPE should not happen.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.