diff --git a/modules/core/src/main/scala/tethys/commons/TokenNode.scala b/modules/core/src/main/scala/tethys/commons/TokenNode.scala index 018313a2..ede41096 100644 --- a/modules/core/src/main/scala/tethys/commons/TokenNode.scala +++ b/modules/core/src/main/scala/tethys/commons/TokenNode.scala @@ -40,6 +40,9 @@ object TokenNode { override val token: Token = NumberValueToken } + case class ByteValueNode(value: Byte) extends TokenNode { + override def token: Token = NumberValueToken + } case class ShortValueNode(value: Short) extends TokenNode { override val token: Token = NumberValueToken } @@ -71,6 +74,7 @@ object TokenNode { def value(v: String): List[TokenNode] = StringValueNode(v) :: Nil def value(v: Boolean): List[TokenNode] = BooleanValueNode(v) :: Nil + def value(v: Byte): List[TokenNode] = ByteValueNode(v) :: Nil def value(v: Short): List[TokenNode] = ShortValueNode(v) :: Nil def value(v: Int): List[TokenNode] = IntValueNode(v) :: Nil def value(v: Long): List[TokenNode] = LongValueNode(v) :: Nil @@ -85,6 +89,7 @@ object TokenNode { case v: TokenNode => v :: Nil case nodes: List[_] => nodes.flatMap(anyToTokens) case v: String => value(v) + case v: Byte => value(v) case v: Short => value(v) case v: Int => value(v) case v: Long => value(v) @@ -115,6 +120,7 @@ object TokenNode { else if (token.isFieldName) FieldNameNode(iterator.fieldName()) else if (token.isStringValue) StringValueNode(iterator.string()) else if (token.isNumberValue) iterator.number() match { + case v: java.lang.Byte => ByteValueNode(v) case v: java.lang.Short => ShortValueNode(v) case v: java.lang.Integer => IntValueNode(v) case v: java.lang.Long => LongValueNode(v) diff --git a/modules/core/src/main/scala/tethys/package.scala b/modules/core/src/main/scala/tethys/package.scala index c525c981..86084882 100644 --- a/modules/core/src/main/scala/tethys/package.scala +++ b/modules/core/src/main/scala/tethys/package.scala @@ -8,7 +8,7 @@ import scala.Specializable.Group package object tethys { - final val specializations = new Group((Short, Int, Long, Float, Double, Boolean)) + final val specializations = new Group((Byte, Short, Int, Long, Float, Double, Boolean)) // given diff --git a/modules/core/src/main/scala/tethys/readers/instances/AllJsonReaders.scala b/modules/core/src/main/scala/tethys/readers/instances/AllJsonReaders.scala index 4b15604d..873c2e30 100644 --- a/modules/core/src/main/scala/tethys/readers/instances/AllJsonReaders.scala +++ b/modules/core/src/main/scala/tethys/readers/instances/AllJsonReaders.scala @@ -47,6 +47,18 @@ trait AllJsonReaders extends OptionReaders { } } + implicit lazy val byteReader: JsonReader[Byte] = new JsonReader[Byte] { + override def read(it: TokenIterator)(implicit fieldName: FieldName): Byte = { + if(it.currentToken().isNumberValue) { + val res = it.byte() + it.next() + res + } else { + ReaderError.wrongJson(s"Expected byte value but found: ${it.currentToken()}") + } + } + } + implicit lazy val shortReader: JsonReader[Short] = new JsonReader[Short] { override def read(it: TokenIterator)(implicit fieldName: FieldName): Short = { if(it.currentToken().isNumberValue) { @@ -112,6 +124,7 @@ trait AllJsonReaders extends OptionReaders { case bi: BigInt => BigDecimal(bi) case jbd: java.math.BigDecimal => BigDecimal(jbd) case jint: java.lang.Integer => BigDecimal(jint) + case jbyte: java.lang.Byte => BigDecimal(jbyte.longValue()) case jshort: java.lang.Short => BigDecimal(jshort.longValue()) case jlong: java.lang.Long => BigDecimal(jlong) case jbi: java.math.BigInteger => BigDecimal(jbi) @@ -126,6 +139,7 @@ trait AllJsonReaders extends OptionReaders { case bd: BigDecimal => bd.toBigInt case jbd: java.math.BigDecimal => jbd.toBigInteger case jint: java.lang.Integer => BigInt(jint) + case jbyte: java.lang.Byte => BigInt(jbyte.longValue()) case jshort: java.lang.Short => BigInt(jshort.longValue()) case jlong: java.lang.Long => BigInt(jlong) case num => BigInt(num.longValue()) @@ -133,6 +147,7 @@ trait AllJsonReaders extends OptionReaders { implicit lazy val javaBooleanReader: JsonReader[java.lang.Boolean] = booleanReader.map(a => a) + implicit lazy val javaByteReader: JsonReader[java.lang.Byte] = byteReader.map(a => a) implicit lazy val javaShortReader: JsonReader[java.lang.Short] = shortReader.map(a => a) implicit lazy val javaIntReader: JsonReader[java.lang.Integer] = intReader.map(a => a) implicit lazy val javaLongReader: JsonReader[java.lang.Long] = longReader.map(a => a) diff --git a/modules/core/src/main/scala/tethys/readers/instances/IterableReaders.scala b/modules/core/src/main/scala/tethys/readers/instances/IterableReaders.scala index e237819c..bedc0761 100644 --- a/modules/core/src/main/scala/tethys/readers/instances/IterableReaders.scala +++ b/modules/core/src/main/scala/tethys/readers/instances/IterableReaders.scala @@ -11,6 +11,13 @@ import scala.language.higherKinds private[tethys] trait IterableReaders extends LowPriorityIterableReaders { + implicit def byteIterableReader[C[X] <: Iterable[X]](implicit + cb: CollectionBuilder[Byte, C[Byte]]): JsonReader[C[Byte]] = new TraversableReader[Byte, C] { + override protected def appendBuilder(it: TokenIterator, builder: mutable.Builder[Byte, C[Byte]])(implicit fieldName: FieldName): Unit = { + builder += PrimitiveReaders.ByteJsonReader.read(it) + } + } + implicit def shortIterableReader[C[X] <: Iterable[X]](implicit cb: CollectionBuilder[Short, C[Short]]): JsonReader[C[Short]] = new TraversableReader[Short, C] { override protected def appendBuilder(it: TokenIterator, builder: mutable.Builder[Short, C[Short]])(implicit fieldName: FieldName): Unit = { diff --git a/modules/core/src/main/scala/tethys/readers/instances/MapReaders.scala b/modules/core/src/main/scala/tethys/readers/instances/MapReaders.scala index 6cd964c0..c04ea438 100644 --- a/modules/core/src/main/scala/tethys/readers/instances/MapReaders.scala +++ b/modules/core/src/main/scala/tethys/readers/instances/MapReaders.scala @@ -10,6 +10,17 @@ import scala.collection.mutable import scala.language.higherKinds private[tethys] trait MapReaders extends LowPriorityMapReaders { + implicit def byteMapReader[K, M[X, Y] <: scala.collection.Map[X, Y]](implicit + keyReader: KeyReader[K], + cb: CollectionBuilder[(K, Byte), M[K, Byte]] + ): JsonReader[M[K, Byte]] = { + new MapReader[K, Byte, M] { + override protected def appendBuilder(it: TokenIterator, builder: mutable.Builder[(K, Byte), M[K, Byte]], key: K)(implicit fieldName: FieldName): Unit = { + builder += key -> PrimitiveReaders.ByteJsonReader.read(it) + } + } + } + implicit def shortMapReader[K, M[X, Y] <: scala.collection.Map[X, Y]](implicit keyReader: KeyReader[K], cb: CollectionBuilder[(K, Short), M[K, Short]] diff --git a/modules/core/src/main/scala/tethys/readers/instances/OptionReaders.scala b/modules/core/src/main/scala/tethys/readers/instances/OptionReaders.scala index c36607b5..b8d003eb 100644 --- a/modules/core/src/main/scala/tethys/readers/instances/OptionReaders.scala +++ b/modules/core/src/main/scala/tethys/readers/instances/OptionReaders.scala @@ -5,6 +5,12 @@ import tethys.readers.FieldName import tethys.readers.tokens.TokenIterator private[tethys] trait OptionReaders extends LowPriorityOptionReaders { + implicit lazy val byteOptionReader: JsonReader[Option[Byte]] = new OptionJsonReader[Byte] { + override protected def readSomeValue(it: TokenIterator)(implicit fieldName: FieldName): Option[Byte] = { + Some(PrimitiveReaders.ByteJsonReader.read(it)) + } + } + implicit lazy val shortOptionReader: JsonReader[Option[Short]] = new OptionJsonReader[Short] { override protected def readSomeValue(it: TokenIterator)(implicit fieldName: FieldName): Option[Short] = { Some(PrimitiveReaders.ShortJsonReader.read(it)) diff --git a/modules/core/src/main/scala/tethys/readers/instances/PrimitiveReaders.scala b/modules/core/src/main/scala/tethys/readers/instances/PrimitiveReaders.scala index c5477afc..660032d5 100644 --- a/modules/core/src/main/scala/tethys/readers/instances/PrimitiveReaders.scala +++ b/modules/core/src/main/scala/tethys/readers/instances/PrimitiveReaders.scala @@ -4,6 +4,18 @@ import tethys.readers.tokens.TokenIterator import tethys.readers.{FieldName, ReaderError} object PrimitiveReaders { + object ByteJsonReader { + def read(it: TokenIterator)(implicit fieldName: FieldName): Byte = { + if(it.currentToken().isNumberValue) { + val res = it.byte() + it.nextToken() + res + } else { + ReaderError.wrongJson(s"Expected byte value but found: ${it.currentToken()}") + } + } + } + object ShortJsonReader { def read(it: TokenIterator)(implicit fieldName: FieldName): Short = { if(it.currentToken().isNumberValue) { diff --git a/modules/core/src/main/scala/tethys/readers/tokens/BaseTokenIterator.scala b/modules/core/src/main/scala/tethys/readers/tokens/BaseTokenIterator.scala index c4731d9a..c013cccf 100644 --- a/modules/core/src/main/scala/tethys/readers/tokens/BaseTokenIterator.scala +++ b/modules/core/src/main/scala/tethys/readers/tokens/BaseTokenIterator.scala @@ -62,6 +62,7 @@ trait BaseTokenIterator extends TokenIterator { else if(token.isFieldName) FieldNameNode(fieldName()) -> 0 else if(token.isStringValue) StringValueNode(string()) -> 0 else if(token.isNumberValue) number() match { + case v: java.lang.Byte => ByteValueNode(v) -> 0 case v: java.lang.Short => ShortValueNode(v) -> 0 case v: java.lang.Integer => IntValueNode(v) -> 0 case v: java.lang.Long => LongValueNode(v) -> 0 diff --git a/modules/core/src/main/scala/tethys/readers/tokens/QueueIterator.scala b/modules/core/src/main/scala/tethys/readers/tokens/QueueIterator.scala index 72157ad0..922bfbf4 100644 --- a/modules/core/src/main/scala/tethys/readers/tokens/QueueIterator.scala +++ b/modules/core/src/main/scala/tethys/readers/tokens/QueueIterator.scala @@ -38,6 +38,7 @@ class QueueIterator(private var nodes: immutable.Queue[TokenNode]) extends BaseT override def number(): Number = nodes.front match { case NumberValueNode(value) => value + case ByteValueNode(value) => value case ShortValueNode(value) => value case IntValueNode(value) => value case LongValueNode(value) => value @@ -46,9 +47,21 @@ class QueueIterator(private var nodes: immutable.Queue[TokenNode]) extends BaseT case node => fail[NumberValueNode](node) } + override def byte(): Byte = nodes.front match { + case ByteValueNode(value) => value + case NumberValueNode(value) => value.byteValue() + case ShortValueNode(value) => value.toByte + case IntValueNode(value) => value.toByte + case LongValueNode(value) => value.toByte + case FloatValueNode(value) => value.toByte + case DoubleValueNode(value) => value.toByte + case node => fail[ByteValueNode](node) + } + override def short(): Short = nodes.front match { case ShortValueNode(value) => value case NumberValueNode(value) => value.shortValue() + case ByteValueNode(value) => value.toShort case IntValueNode(value) => value.toShort case LongValueNode(value) => value.toShort case FloatValueNode(value) => value.toShort @@ -59,6 +72,7 @@ class QueueIterator(private var nodes: immutable.Queue[TokenNode]) extends BaseT override def int(): Int = nodes.front match { case IntValueNode(value) => value case NumberValueNode(value) => value.intValue() + case ByteValueNode(value) => value.toInt case ShortValueNode(value) => value.toInt case LongValueNode(value) => value.toInt case FloatValueNode(value) => value.toInt @@ -69,6 +83,7 @@ class QueueIterator(private var nodes: immutable.Queue[TokenNode]) extends BaseT override def long(): Long = nodes.front match { case LongValueNode(value) => value case NumberValueNode(value) => value.longValue() + case ByteValueNode(value) => value.toLong case ShortValueNode(value) => value.toLong case IntValueNode(value) => value.toLong case FloatValueNode(value) => value.toLong @@ -79,6 +94,7 @@ class QueueIterator(private var nodes: immutable.Queue[TokenNode]) extends BaseT override def float(): Float = nodes.front match { case FloatValueNode(value) => value case NumberValueNode(value) => value.floatValue() + case ByteValueNode(value) => value.toFloat case ShortValueNode(value) => value.toFloat case IntValueNode(value) => value.toFloat case LongValueNode(value) => value.toFloat @@ -89,6 +105,7 @@ class QueueIterator(private var nodes: immutable.Queue[TokenNode]) extends BaseT override def double(): Double = nodes.front match { case DoubleValueNode(value) => value case NumberValueNode(value) => value.doubleValue() + case ByteValueNode(value) => value.toDouble case ShortValueNode(value) => value.toDouble case IntValueNode(value) => value.toDouble case LongValueNode(value) => value.toDouble diff --git a/modules/core/src/main/scala/tethys/readers/tokens/TokenIterator.scala b/modules/core/src/main/scala/tethys/readers/tokens/TokenIterator.scala index 746a1dc7..6c7448ce 100644 --- a/modules/core/src/main/scala/tethys/readers/tokens/TokenIterator.scala +++ b/modules/core/src/main/scala/tethys/readers/tokens/TokenIterator.scala @@ -17,6 +17,8 @@ trait TokenIterator { def number(): Number + def byte(): Byte + def short(): Short def int(): Int diff --git a/modules/core/src/main/scala/tethys/writers/instances/AllJsonWriters.scala b/modules/core/src/main/scala/tethys/writers/instances/AllJsonWriters.scala index 5ff10408..11211d45 100644 --- a/modules/core/src/main/scala/tethys/writers/instances/AllJsonWriters.scala +++ b/modules/core/src/main/scala/tethys/writers/instances/AllJsonWriters.scala @@ -12,6 +12,10 @@ trait AllJsonWriters extends OptionWriters with EitherWriters { override def write(value: Long, tokenWriter: TokenWriter): Unit = tokenWriter.writeNumber(value) } + implicit lazy val byteWriter: JsonWriter[Byte] = new JsonWriter[Byte] { + override def write(value: Byte, tokenWriter: TokenWriter): Unit = tokenWriter.writeNumber(value) + } + implicit lazy val shortWriter: JsonWriter[Short] = new JsonWriter[Short] { override def write(value: Short, tokenWriter: TokenWriter): Unit = tokenWriter.writeNumber(value) } @@ -52,6 +56,10 @@ trait AllJsonWriters extends OptionWriters with EitherWriters { override def write(value: java.lang.Long, tokenWriter: TokenWriter): Unit = tokenWriter.writeNumber(value) } + implicit lazy val javaByteWriter: JsonWriter[java.lang.Byte] = new JsonWriter[java.lang.Byte] { + override def write(value: java.lang.Byte, tokenWriter: TokenWriter): Unit = tokenWriter.writeNumber(value) + } + implicit lazy val javaShortWriter: JsonWriter[java.lang.Short] = new JsonWriter[java.lang.Short] { override def write(value: java.lang.Short, tokenWriter: TokenWriter): Unit = tokenWriter.writeNumber(value) } diff --git a/modules/core/src/main/scala/tethys/writers/tokens/SimpleTokenWriter.scala b/modules/core/src/main/scala/tethys/writers/tokens/SimpleTokenWriter.scala index c3827e16..c70b3142 100644 --- a/modules/core/src/main/scala/tethys/writers/tokens/SimpleTokenWriter.scala +++ b/modules/core/src/main/scala/tethys/writers/tokens/SimpleTokenWriter.scala @@ -22,6 +22,8 @@ class SimpleTokenWriter extends TokenWriter { override def writeString(v: String): SimpleTokenWriter.this.type = append(StringValueNode(v)) + override def writeNumber(v: Byte): SimpleTokenWriter.this.type = append(ByteValueNode(v)) + override def writeNumber(v: Short): SimpleTokenWriter.this.type = append(ShortValueNode(v)) override def writeNumber(v: Int): SimpleTokenWriter.this.type = append(IntValueNode(v)) diff --git a/modules/core/src/main/scala/tethys/writers/tokens/TokenWriter.scala b/modules/core/src/main/scala/tethys/writers/tokens/TokenWriter.scala index 834a109c..c8b09755 100644 --- a/modules/core/src/main/scala/tethys/writers/tokens/TokenWriter.scala +++ b/modules/core/src/main/scala/tethys/writers/tokens/TokenWriter.scala @@ -13,6 +13,8 @@ trait TokenWriter { def writeString(v: String): this.type + def writeNumber(v: Byte): this.type + def writeNumber(v: Short): this.type def writeNumber(v: Int): this.type @@ -30,6 +32,7 @@ trait TokenWriter { def writeRawNumber(n: Number): this.type = n match { case jbd: java.math.BigDecimal => writeNumber(BigDecimal(jbd)) case jint: java.lang.Integer => writeNumber(jint.intValue()) + case jbyte: java.lang.Byte => writeNumber(jbyte.longValue()) case jshort: java.lang.Short => writeNumber(jshort.longValue()) case jlong: java.lang.Long => writeNumber(jlong.longValue()) case jbi: java.math.BigInteger => writeNumber(BigInt(jbi)) diff --git a/modules/core/src/test/scala/tethys/readers/DefaultReadersTest.scala b/modules/core/src/test/scala/tethys/readers/DefaultReadersTest.scala index 48226e48..4f37c94d 100644 --- a/modules/core/src/test/scala/tethys/readers/DefaultReadersTest.scala +++ b/modules/core/src/test/scala/tethys/readers/DefaultReadersTest.scala @@ -26,6 +26,7 @@ class DefaultReadersTest extends AnyFlatSpec { test("1") -> value("1"), test('1') -> value("1"), test(1) -> value(1), + test(1: Byte) -> value(1: Byte), test(1: Short) -> value(1: Short), test(1L) -> value(1L), test(1f) -> value(1f), @@ -42,6 +43,7 @@ class DefaultReadersTest extends AnyFlatSpec { test(Option(1), "Option.nonEmpty") -> value(1), test(Option.empty[Int], "Option.empty") -> List(NullValueNode) , test(1: java.lang.Integer) -> value(1), + test(java.lang.Byte.valueOf(1: Byte)) -> value(1: Byte), test(java.lang.Short.valueOf(1: Short)) -> value(1: Short), test(1L: java.lang.Long) -> value(1L), test(1f: java.lang.Float) -> value(1f), diff --git a/modules/core/src/test/scala/tethys/writers/DefaultWritersTest.scala b/modules/core/src/test/scala/tethys/writers/DefaultWritersTest.scala index 577f8112..392ee0e3 100644 --- a/modules/core/src/test/scala/tethys/writers/DefaultWritersTest.scala +++ b/modules/core/src/test/scala/tethys/writers/DefaultWritersTest.scala @@ -25,6 +25,7 @@ class DefaultWritersTest extends AnyFlatSpec { test("1") -> value("1"), test('1') -> value("1"), test(1) -> value(1), + test(1: Byte) -> value(1: Byte), test(1: Short) -> value(1: Short), test(1L) -> value(1L), test(1f) -> value(1f), @@ -44,6 +45,7 @@ class DefaultWritersTest extends AnyFlatSpec { test(Right(1): Either[String, Int], "Either.right") -> value(1), test(Left("Not an Int"): Either[String, Int], "Either.left") -> value("Not an Int"), test(1: java.lang.Integer) -> value(1), + test(java.lang.Byte.valueOf(1: Byte)) -> value(1: Byte), test(java.lang.Short.valueOf(1: Short)) -> value(1: Short), test(1L: java.lang.Long) -> value(1L), test(1f: java.lang.Float) -> value(1f), diff --git a/modules/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenIterator.scala b/modules/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenIterator.scala index 7a0ed4fc..6833f17d 100644 --- a/modules/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenIterator.scala +++ b/modules/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenIterator.scala @@ -26,6 +26,8 @@ final class JacksonTokenIterator(jsonParser: JsonParser) extends BaseTokenIterat override def number(): Number = jsonParser.getNumberValue + override def byte(): Byte = jsonParser.getByteValue + override def short(): Short = jsonParser.getShortValue override def int(): Int = jsonParser.getIntValue diff --git a/modules/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenWriter.scala b/modules/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenWriter.scala index 0102b094..53497f7f 100644 --- a/modules/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenWriter.scala +++ b/modules/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenWriter.scala @@ -34,6 +34,11 @@ class JacksonTokenWriter(jsonGenerator: JsonGenerator) extends TokenWriter { this } + override def writeNumber(v: Byte): JacksonTokenWriter.this.type = { + jsonGenerator.writeNumber(v) + this + } + override def writeNumber(v: Short): JacksonTokenWriter.this.type = { jsonGenerator.writeNumber(v) this diff --git a/modules/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenWriterTest.scala b/modules/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenWriterTest.scala index e8805401..cf51f79a 100644 --- a/modules/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenWriterTest.scala +++ b/modules/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenWriterTest.scala @@ -23,6 +23,10 @@ class JacksonTokenWriterTest extends AnyFlatSpec with Matchers { iterate(_.writeString("string")) shouldBe """"string"""" } + it should "write Byte value" in { + iterate(_.writeNumber(1: Byte)) shouldBe """1""" + } + it should "write Short value" in { iterate(_.writeNumber(1: Short)) shouldBe """1""" }