com-lihaoyi / upickle

uPickle: a simple, fast, dependency-free JSON & Binary (MessagePack) serialization library for Scala
https://com-lihaoyi.github.io/upickle
MIT License
717 stars 164 forks source link

Correction re: play-json #4

Closed stanch closed 10 years ago

stanch commented 10 years ago

Jackson is used for parsing only. As far as I know, that does not employ reflection.

lihaoyi commented 10 years ago

As far as I knew it didn't either, but when I tried locking down scala-js-fiddle with a security manager that prohibited reflection, play-json broke =( I don't know how or why, but I'm pretty sure it uses reflection for serialization. All I was trying to do was serialize Seqs and Strings to json and send them to the client

stanch commented 10 years ago

That’s weird... Maybe @jroper, @mandubian or @jto could comment on that? Otherwise I’d appear to be spreading fud...

lihaoyi commented 10 years ago

FWIW here's a repro

// project/build.sbt
addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2")
// build.sbt
resolvers += "Typesafe Repo" at "http://repo.typesafe.com/typesafe/releases/"

scalaVersion := "2.10.2"

libraryDependencies += "play" % "play_2.10" % "2.1.0"

Revolver.settings
// Run.scala
import play.api.libs.json._
object Run{
  def main(args: Array[String]): Unit = {
    println("Hello")
    System.setSecurityManager(new SecurityManager());
    println(Json.stringify(Json.obj("name" -> "omg")))
  }
}

Output:

haoyi-mbp:test haoyi$ sbt
[info] Loading global plugins from /Users/haoyi/.sbt/0.13/plugins
[info] Loading project definition from /Users/haoyi/Dropbox (Personal)/Workspace/test/project
[info] Set current project to test (in build file:/Users/haoyi/Dropbox%20(Personal)/Workspace/test/)
> re-start
[info] Application test not yet started
[info] Starting application test in the background ...
test Starting Run.main()
[success] Total time: 0 s, completed Jul 20, 2014 4:59:34 PM
> test Hello
test[ERROR] java.security.AccessControlException: access denied (java.lang.RuntimePermission accessDeclaredMembers)
test[ERROR]     at java.security.AccessControlContext.checkPermission(AccessControlContext.java:376)
test[ERROR]     at java.security.AccessController.checkPermission(AccessController.java:549)
test[ERROR]     at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
test[ERROR]     at java.lang.SecurityManager.checkMemberAccess(SecurityManager.java:1662)
test[ERROR]     at java.lang.Class.checkMemberAccess(Class.java:2195)
test[ERROR]     at java.lang.Class.getDeclaredMethods(Class.java:1826)
test[ERROR]     at org.codehaus.jackson.map.introspect.AnnotatedClass._addMemberMethods(AnnotatedClass.java:620)
test[ERROR]     at org.codehaus.jackson.map.introspect.AnnotatedClass.resolveMemberMethods(AnnotatedClass.java:413)
test[ERROR]     at org.codehaus.jackson.map.introspect.BasicClassIntrospector.classWithCreators(BasicClassIntrospector.java:185)
test[ERROR]     at org.codehaus.jackson.map.introspect.BasicClassIntrospector.collectProperties(BasicClassIntrospector.java:157)
test[ERROR]     at org.codehaus.jackson.map.introspect.BasicClassIntrospector.forSerialization(BasicClassIntrospector.java:96)
test[ERROR]     at org.codehaus.jackson.map.introspect.BasicClassIntrospector.forSerialization(BasicClassIntrospector.java:16)
test[ERROR]     at org.codehaus.jackson.map.SerializationConfig.introspect(SerializationConfig.java:973)
test[ERROR]     at org.codehaus.jackson.map.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:251)
test[ERROR]     at org.codehaus.jackson.map.ser.StdSerializerProvider._createUntypedSerializer(StdSerializerProvider.java:782)
test[ERROR]     at org.codehaus.jackson.map.ser.StdSerializerProvider._createAndCacheUntypedSerializer(StdSerializerProvider.java:735)
test[ERROR]     at org.codehaus.jackson.map.ser.StdSerializerProvider.findValueSerializer(StdSerializerProvider.java:344)
test[ERROR]     at org.codehaus.jackson.map.ser.StdSerializerProvider.findTypedValueSerializer(StdSerializerProvider.java:420)
test[ERROR]     at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:601)
test[ERROR]     at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:256)
test[ERROR]     at org.codehaus.jackson.map.ObjectMapper.writeValue(ObjectMapper.java:1613)
test[ERROR]     at play.api.libs.json.JacksonJson$.generateFromJsValue(JsValue.scala:483)
test[ERROR]     at play.api.libs.json.Json$.stringify(Json.scala:35)
test[ERROR]     at Run$.main(Run.scala:7)
test[ERROR]     at Run.main(Run.scala)
test[ERROR]     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
test[ERROR]     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
test[ERROR]     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
test[ERROR]     at java.lang.reflect.Method.invoke(Method.java:597)
test[ERROR]     at scala.tools.nsc.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:71)
test[ERROR]     at scala.tools.nsc.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:31)
test[ERROR]     at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:139)
test[ERROR]     at scala.tools.nsc.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:71)
test[ERROR]     at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:139)
test[ERROR]     at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:28)
test[ERROR]     at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:45)
test[ERROR]     at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:35)
test[ERROR]     at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:45)
test[ERROR]     at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:74)
test[ERROR]     at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
test[ERROR]     at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:105)
test[ERROR]     at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
test ... finished with exit code 1

I mean, I would also like it if something simple like "converting JSON structure to string" didn't require reflection. If it didn't though, I probably wouldn't have made this library in the first place =P

stanch commented 10 years ago

Dayum! This is beyond awful :( On the other hand, why not just create your own Json.stringify?

lihaoyi commented 10 years ago

I did, but why not create my own JSON ast too? It's like 10 lines of code. So I did, and that's the AST that sits underneath uPickle's serialization format =P No point depending on the entirely of play-json and all its stuff just for a 10-line JSON ast

stanch commented 10 years ago

Fair enough! It just discourages me that there are already around 10 JSON libraries in the ecosystem, and each of them has approximately one killer feature: dijon uses Dynamic, argonaut has cool zippers, spray-json is “simple” and typeclass-based, play-json has macros for generation of typeclass instances and rule composition, json4s has “universal” AST, rapture has json string interpolator, scala-pickling is “boilerplate-free”, etc — but unfortunately every library brings in some downsides in whatever it doesn’t strive to do well, like some don’t use typeclasses, some use a crappy parser/serializer, etc. Don’t get me wrong, competition is cool, but right now it’s more like the Tower of Babel :) Maybe it would be helpful to separate the steps (parsing, AST, manipulation, validation, morphisms) and cultivate some de-facto standards. json4s seemed to be a good effort, but for some reason it isn’t widely adopted by library writers...

P.S. Have you considered changing your name to Li “µ” Haoyi? :)

mandubian commented 10 years ago

The issue isn't play-json but jackson... When play2.1 was released, it was the fastest json serializer...