From 3574c89606cb1e1ec96be7d5d67932f158d61fdc Mon Sep 17 00:00:00 2001 From: thinkharder Date: Mon, 8 Mar 2021 13:19:14 -0500 Subject: [PATCH 1/3] Add minimal test case for tab encoding issue --- .../shared/src/test/scala/zio/json/CodecSpec.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala b/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala index 7732baa75..eb20ea3b5 100644 --- a/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala +++ b/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala @@ -111,9 +111,19 @@ object CodecSpec extends DefaultRunnableSpec { assert(jsonStr.fromJson[Chunk[String]])(isRight(equalTo(expected))) } + ), + suite("Encode -> Decode")( + test("char") { + assert(encodeDecode(JsonCodec.char, '\u0009'))(isRight(equalTo('\u0009'))) + } ) ) + private def encodeDecode[A](codec: JsonCodec[A], value: A): Either[String, A] = + codec.decodeJson( + codec.encodeJson(value, None) + ) + object exampleproducts { case class Parameterless() From 386be0e9f5c1ef8dab5e9a40d532d42d478aee15 Mon Sep 17 00:00:00 2001 From: thinkharder Date: Mon, 8 Mar 2021 13:44:29 -0500 Subject: [PATCH 2/3] Create separate char codec for handling control characters --- .../src/main/scala/zio/json/encoder.scala | 20 ++++++++++++-- .../src/test/scala/zio/json/CodecSpec.scala | 26 ++++++++++++++++--- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/zio-json/shared/src/main/scala/zio/json/encoder.scala b/zio-json/shared/src/main/scala/zio/json/encoder.scala index c78132d92..3787ab217 100644 --- a/zio-json/shared/src/main/scala/zio/json/encoder.scala +++ b/zio-json/shared/src/main/scala/zio/json/encoder.scala @@ -106,6 +106,22 @@ object JsonEncoder extends GeneratedTupleEncoders with EncoderLowPriority0 { } + implicit val char: JsonEncoder[Char] = new JsonEncoder[Char] { + + override def unsafeEncode(a: Char, indent: Option[Int], out: Write): Unit = { + out.write('"') + a match { + case '"' => out.write("\\\"") + case '\\' => out.write("\\\\") + case c => + if (c < ' ') out.write("\\u%04x".format(c.toInt)) + else out.write(c.toString) + } + out.write('"') + } + + } + private[json] def explicit[A](f: A => String): JsonEncoder[A] = new JsonEncoder[A] { def unsafeEncode(a: A, indent: Option[Int], out: Write): Unit = out.write(f(a)) } @@ -114,8 +130,8 @@ object JsonEncoder extends GeneratedTupleEncoders with EncoderLowPriority0 { def unsafeEncode(a: A, indent: Option[Int], out: Write): Unit = out.write(s""""${f(a)}"""") } - implicit val boolean: JsonEncoder[Boolean] = explicit(_.toString) - implicit val char: JsonEncoder[Char] = string.contramap(_.toString) + implicit val boolean: JsonEncoder[Boolean] = explicit(_.toString) +// implicit val char: JsonEncoder[Char] = string.contramap(_.toString) implicit val symbol: JsonEncoder[Symbol] = string.contramap(_.name) implicit val byte: JsonEncoder[Byte] = explicit(_.toString) implicit val short: JsonEncoder[Short] = explicit(_.toString) diff --git a/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala b/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala index eb20ea3b5..31ea1e56a 100644 --- a/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala +++ b/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala @@ -113,9 +113,29 @@ object CodecSpec extends DefaultRunnableSpec { } ), suite("Encode -> Decode")( - test("char") { - assert(encodeDecode(JsonCodec.char, '\u0009'))(isRight(equalTo('\u0009'))) - } + suite("control chars")( + test("tab") { + assert(encodeDecode(JsonCodec.char, '\t'))(isRight(equalTo('\t'))) + }, + test("carriage return") { + assert(encodeDecode(JsonCodec.char, '\r'))(isRight(equalTo('\r'))) + }, + test("newline") { + assert(encodeDecode(JsonCodec.char, '\n'))(isRight(equalTo('\n'))) + }, + test("form feed") { + assert(encodeDecode(JsonCodec.char, '\f'))(isRight(equalTo('\f'))) + }, + test("backspace") { + assert(encodeDecode(JsonCodec.char, '\b'))(isRight(equalTo('\b'))) + }, + test("escape") { + assert(encodeDecode(JsonCodec.char, '\\'))(isRight(equalTo('\\'))) + }, + test("quote") { + assert(encodeDecode(JsonCodec.char, '"'))(isRight(equalTo('"'))) + } + ) ) ) From f0e866e77bb343668800d3dcd4387f6d87016e2b Mon Sep 17 00:00:00 2001 From: thinkharder Date: Mon, 8 Mar 2021 13:48:17 -0500 Subject: [PATCH 3/3] Remove commented --- zio-json/shared/src/main/scala/zio/json/encoder.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zio-json/shared/src/main/scala/zio/json/encoder.scala b/zio-json/shared/src/main/scala/zio/json/encoder.scala index 3787ab217..a611822bc 100644 --- a/zio-json/shared/src/main/scala/zio/json/encoder.scala +++ b/zio-json/shared/src/main/scala/zio/json/encoder.scala @@ -130,8 +130,7 @@ object JsonEncoder extends GeneratedTupleEncoders with EncoderLowPriority0 { def unsafeEncode(a: A, indent: Option[Int], out: Write): Unit = out.write(s""""${f(a)}"""") } - implicit val boolean: JsonEncoder[Boolean] = explicit(_.toString) -// implicit val char: JsonEncoder[Char] = string.contramap(_.toString) + implicit val boolean: JsonEncoder[Boolean] = explicit(_.toString) implicit val symbol: JsonEncoder[Symbol] = string.contramap(_.name) implicit val byte: JsonEncoder[Byte] = explicit(_.toString) implicit val short: JsonEncoder[Short] = explicit(_.toString)