Skip to content

Commit

Permalink
JsonFieldEncoder for uuid (#1144)
Browse files Browse the repository at this point in the history
* JsonFieldEncoder for uuid

* unused

* formatting

---------

Co-authored-by: Petter Kamfjord <petter.kamfjord@trainor.no>
  • Loading branch information
Petter-K and PetterKa authored Aug 20, 2024
1 parent 0a555ac commit 6395b8e
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 0 deletions.
20 changes: 20 additions & 0 deletions zio-json/shared/src/main/scala/zio/json/JsonFieldDecoder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package zio.json

import zio.json.uuid.UUIDParser

/** When decoding a JSON Object, we only allow the keys that implement this interface. */
trait JsonFieldDecoder[+A] {
self =>
Expand Down Expand Up @@ -64,4 +66,22 @@ object JsonFieldDecoder {
case n: NumberFormatException => Left(s"Invalid Long: '$str': $n")
}
}

implicit val uuid: JsonFieldDecoder[java.util.UUID] = mapStringOrFail { str =>
try {
Right(UUIDParser.unsafeParse(str))
} catch {
case iae: IllegalArgumentException => Left(s"Invalid UUID: ${iae.getMessage}")
}
}

// use this instead of `string.mapOrFail` in supertypes (to prevent class initialization error at runtime)
private[json] def mapStringOrFail[A](f: String => Either[String, A]): JsonFieldDecoder[A] =
new JsonFieldDecoder[A] {
def unsafeDecodeField(trace: List[JsonError], in: String): A =
f(string.unsafeDecodeField(trace, in)) match {
case Left(err) => throw JsonDecoder.UnsafeJson(JsonError.Message(err) :: trace)
case Right(value) => value
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,8 @@ object JsonFieldEncoder {

implicit val long: JsonFieldEncoder[Long] =
JsonFieldEncoder[String].contramap(_.toString)

implicit val uuid: JsonFieldEncoder[java.util.UUID] =
JsonFieldEncoder[String].contramap(_.toString)

}
39 changes: 39 additions & 0 deletions zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,45 @@ object DecoderSpec extends ZIOSpecDefault {
val jsonStr = JsonEncoder[Map[String, String]].encodeJson(expected, None)
assert(jsonStr.fromJson[Map[String, String]])(isRight(equalTo(expected)))
},
test("Map with UUID keys") {
def expectedMap(str: String): Map[UUID, String] = Map(UUID.fromString(str) -> "value")

val ok1 = """{"64d7c38d-2afd-4514-9832-4e70afe4b0f8": "value"}"""
val ok2 = """{"0000000064D7C38D-FD-14-32-70AFE4B0f8": "value"}"""
val ok3 = """{"0-0-0-0-0": "value"}"""
val bad1 = """{"": "value"}"""
val bad2 = """{"64d7c38d-2afd-4514-9832-4e70afe4b0f80": "value"}"""
val bad3 = """{"64d7c38d-2afd-4514-983-4e70afe4b0f80": "value"}"""
val bad4 = """{"64d7c38d-2afd--9832-4e70afe4b0f8": "value"}"""
val bad5 = """{"64d7c38d-2afd-XXXX-9832-4e70afe4b0f8": "value"}"""
val bad6 = """{"64d7c38d-2afd-X-9832-4e70afe4b0f8": "value"}"""
val bad7 = """{"0-0-0-0-00000000000000000": "value"}"""

assert(ok1.fromJson[Map[UUID, String]])(
isRight(equalTo(expectedMap("64d7c38d-2afd-4514-9832-4e70afe4b0f8")))
) &&
assert(ok2.fromJson[Map[UUID, String]])(
isRight(equalTo(expectedMap("64D7C38D-00FD-0014-0032-0070AfE4B0f8")))
) &&
assert(ok3.fromJson[Map[UUID, String]])(
isRight(equalTo(expectedMap("00000000-0000-0000-0000-000000000000")))
) &&
assert(bad1.fromJson[Map[UUID, String]])(isLeft(containsString("Invalid UUID: "))) &&
assert(bad2.fromJson[Map[UUID, String]])(isLeft(containsString("Invalid UUID: UUID string too large"))) &&
assert(bad3.fromJson[Map[UUID, String]])(
isLeft(containsString("Invalid UUID: 64d7c38d-2afd-4514-983-4e70afe4b0f80"))
) &&
assert(bad4.fromJson[Map[UUID, String]])(
isLeft(containsString("Invalid UUID: 64d7c38d-2afd--9832-4e70afe4b0f8"))
) &&
assert(bad5.fromJson[Map[UUID, String]])(
isLeft(containsString("Invalid UUID: 64d7c38d-2afd-XXXX-9832-4e70afe4b0f8"))
) &&
assert(bad6.fromJson[Map[UUID, String]])(
isLeft(containsString("Invalid UUID: 64d7c38d-2afd-X-9832-4e70afe4b0f8"))
) &&
assert(bad7.fromJson[Map[UUID, String]])(isLeft(containsString("Invalid UUID: 0-0-0-0-00000000000000000")))
},
test("zio.Chunk") {
val jsonStr = """["5XL","2XL","XL"]"""
val expected = Chunk("5XL", "2XL", "XL")
Expand Down
5 changes: 5 additions & 0 deletions zio-json/shared/src/test/scala/zio/json/EncoderSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,11 @@ object EncoderSpec extends ZIOSpecDefault {
test("Map, custom keys") {
assert(Map(1 -> "a").toJson)(equalTo("""{"1":"a"}"""))
},
test("Map, UUID keys") {
assert(Map(UUID.fromString("e142f1aa-6e9e-4352-adfe-7e6eb9814ccd") -> "abcd").toJson)(
equalTo("""{"e142f1aa-6e9e-4352-adfe-7e6eb9814ccd":"abcd"}""")
)
},
test("java.util.UUID") {
assert(UUID.fromString("e142f1aa-6e9e-4352-adfe-7e6eb9814ccd").toJson)(
equalTo(""""e142f1aa-6e9e-4352-adfe-7e6eb9814ccd"""")
Expand Down

0 comments on commit 6395b8e

Please sign in to comment.