From ef6d8a21849a97cdfc63c5cb740dd55f24807a48 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 28 Oct 2014 13:23:35 +0100 Subject: Add member name unmangling to ProductFormats, fixes #120 --- src/main/scala/spray/json/ProductFormats.scala | 27 +++++++++++++++++++++- src/test/scala/spray/json/ProductFormatsSpec.scala | 13 +++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/scala/spray/json/ProductFormats.scala b/src/main/scala/spray/json/ProductFormats.scala index 5f5a9f4..39d5300 100644 --- a/src/main/scala/spray/json/ProductFormats.scala +++ b/src/main/scala/spray/json/ProductFormats.scala @@ -78,12 +78,37 @@ trait ProductFormats extends ProductFormatsInstances { sys.error("Case class " + clazz.getName + " declares additional fields") if (fields.zip(copyDefaultMethods).exists { case (f, m) => f.getType != m.getReturnType }) sys.error("Cannot determine field order of case class " + clazz.getName) - fields.map(_.getName) + fields.map(f => ProductFormats.unmangle(f.getName)) } catch { case NonFatal(ex) => throw new RuntimeException("Cannot automatically determine case class field names and order " + "for '" + clazz.getName + "', please use the 'jsonFormat' overload with explicit field name specification", ex) } } + +} + +object ProductFormats { + private val operators = Map( + "$eq" -> "=", + "$greater" -> ">", + "$less" -> "<", + "$plus" -> "+", + "$minus" -> "-", + "$times" -> "*", + "$div" -> "/", + "$bang" -> "!", + "$at" -> "@", + "$hash" -> "#", + "$percent" -> "%", + "$up" -> "^", + "$amp" -> "&", + "$tilde" -> "~", + "$qmark" -> "?", + "$bar" -> "|") + + private def unmangle(name: String) = operators.foldLeft(name) { case (n, (mangled, unmangled)) => + if (n.indexOf(mangled) >= 0) n.replace(mangled, unmangled) else n + } } /** diff --git a/src/test/scala/spray/json/ProductFormatsSpec.scala b/src/test/scala/spray/json/ProductFormatsSpec.scala index c4bb489..a290604 100644 --- a/src/test/scala/spray/json/ProductFormatsSpec.scala +++ b/src/test/scala/spray/json/ProductFormatsSpec.scala @@ -28,6 +28,7 @@ class ProductFormatsSpec extends Specification { } @SerialVersionUID(1L) // SerialVersionUID adds a static field to the case class case class TestStatic(a: Int, b: Option[Double]) + case class TestMangled(`foo-bar!`: Int) trait TestProtocol { this: DefaultJsonProtocol => @@ -36,6 +37,7 @@ class ProductFormatsSpec extends Specification { implicit def test3Format[A: JsonFormat, B: JsonFormat] = jsonFormat2(Test3.apply[A, B]) implicit def testTransientFormat = jsonFormat2(TestTransient) implicit def testStaticFormat = jsonFormat2(TestStatic) + implicit def testMangledFormat = jsonFormat1(TestMangled) } object TestProtocol1 extends DefaultJsonProtocol with TestProtocol object TestProtocol2 extends DefaultJsonProtocol with TestProtocol with NullOptions @@ -181,4 +183,15 @@ class ProductFormatsSpec extends Specification { JsNull.convertTo[Test0] must throwA(new DeserializationException("Object expected")) ) } + + "A JsonFormat created with `jsonFormat`, for a case class with mangled-name members," should { + import TestProtocol1._ + val json = "{\"foo-bar!\":42}" + "produce the correct JSON" in { + TestMangled(42).toJson.compactPrint === json + } + "convert a JsObject to the respective case class instance" in { + json.parseJson.convertTo[TestMangled] === TestMangled(42) + } + } } -- cgit v1.2.3