plokhotnyuk on master
More efficient parsing of doubl… (compare)
plokhotnyuk on master
Code clean up (compare)
plokhotnyuk on master
More efficient parsing of doubl… (compare)
plokhotnyuk on master
Clean up of code (compare)
plokhotnyuk on master
Clean up of code (compare)
plokhotnyuk on master
Clean up of code (compare)
plokhotnyuk on master
Clean up of code (compare)
plokhotnyuk on master
Clean up of code (compare)
plokhotnyuk on master
Update jackson-module-scala to … (compare)
plokhotnyuk on master
Update jackson-module-afterburn… (compare)
plokhotnyuk on master
Update jackson-datatype-jdk8 to… (compare)
plokhotnyuk on gh-pages
Update JVM results for v2.13.22 (compare)
plokhotnyuk on v2.13.22
plokhotnyuk on master
Setting version to 2.13.22 Setting version to 2.13.23-SNAP… (compare)
ReaderConfig
everywhere, manually. I guess that's less than optimal.
def read[A](buf: Array[Byte])(implicit codec: JsonValueCodec[A], config: ReaderConfig = CustomReaderConfig) = readFromArray(buf, config)(codec)
make
only for the top level type of your data structure as described in the README example. All subsequent codecs will be automatically generated and inlined to the top level one. Defining of type class instances for other types of messages by calling make(cfg: CodecMakerConfig)
could be needed if the derivation configuration should be different from the default or custom configuration used for the top level type.
withFieldNameMapper
but I'm still getting PascalCase
JsonCodecMaker.make(
CodecMakerConfig
.withDiscriminatorFieldName(None)
.withFieldNameMapper(JsonCodecMaker.`enforce-kebab-case`)
)
.withAdtLeafClassNameMapper(c => JsonCodecMaker.`enforce-kebab-case`(JsonCodecMaker.simpleClassName(c)))
@/all
Macros API of jsoniter-scala got Scala 3 support! Binaries are available on the Maven Central
It is source compatible with Scala 2.x versions so migration to Scala 3 or adding Scala 3 cross build should be smooth for most usages.
All credits to @rssh for his amazing work!!!
I'm trying to migrate a play-json codec to jsoniter... the play codec looks like
implicit val attributeWrites: Writes[WebCreateAssetAttribute[_]] = Writes {
case x: WebCreateDataTableAssetAttribute => dataTableAttributeWrites.writes(x)
case x: WebCreateStringAssetAttribute => stringAttributeWrites.writes(x)
case _ => throw new IllegalStateException("unknown type")
}
implicit val attributeReads: Reads[WebCreateAssetAttribute[_]] = Reads { jsValue =>
(jsValue \ "type").as[String] match {
case x if x.startsWith("datatable") => dataTableAttributeReads.reads(jsValue)
case _ => stringAttributeReads.reads(jsValue)
}
}
I can do the encode with a custom codec, but how can I do the decode ? e.g. how do I peek into the json object to read the type
and then delegate to the corresponding codec?
implicit val stringAssetAttributeCodec: JsonValueCodec[WebCreateStringAssetAttribute] = JsonCodecMaker.make[WebCreateStringAssetAttribute](CodecMakerConfig.withTransientNone(false).withTransientEmpty(false).withTransientDefault(false))
implicit val dataTableAssetAttributeCodec: JsonValueCodec[WebCreateDataTableAssetAttribute] = JsonCodecMaker.make[WebCreateDataTableAssetAttribute](CodecMakerConfig.withTransientNone(false).withTransientEmpty(false).withTransientDefault(false))
implicit val assetAttributeCodec: JsonValueCodec[WebCreateAssetAttribute[_]] = new JsonValueCodec[WebCreateAssetAttribute[_]]{
override def decodeValue(in: JsonReader, default: WebCreateAssetAttribute[_]): WebCreateAssetAttribute[_] = {
// how can I peek into the `type` of the json object and then decide how to decode it?
val typeField: String = ???
typeField match {
case "string" => stringAssetAttributeCodec.decodeValue(in, default.asInstanceOf[WebCreateStringAssetAttribute])
case x if x.startsWith("dataTable") => dataTableAssetAttributeCodec.decodeValue(in, default.asInstanceOf[WebCreateDataTableAssetAttribute])
case _ => ???
}
}
override def encodeValue(x: WebCreateAssetAttribute[_], out: JsonWriter): Unit = x match {
case v: WebCreateStringAssetAttribute => stringAssetAttributeCodec.encodeValue(v, out)
case v: WebCreateDataTableAssetAttribute => dataTableAssetAttributeCodec.encodeValue(v, out)
}
override def nullValue: WebCreateAssetAttribute[_] = null.asInstanceOf[WebCreateAssetAttribute[_]]
}
setMark
rollbackToMark
and skipToKey
implicit val assetAttributeCodec: JsonValueCodec[WebCreateAssetAttribute] = new JsonValueCodec[WebCreateAssetAttribute]{
override def decodeValue(in: JsonReader, default: WebCreateAssetAttribute): WebCreateAssetAttribute = {
in.setMark()
in.nextToken()
in.skipToKey("type")
val discriminator = in.readString("")
discriminator match {
case "string" =>
in.rollbackToMark()
stringAssetAttributeCodec.decodeValue(in, default.asInstanceOf[WebCreateStringAssetAttribute])
case x if x.startsWith("datatable") =>
in.rollbackToMark()
dataTableAssetAttributeCodec.decodeValue(in, default.asInstanceOf[WebCreateDataTableAssetAttribute])
case _ => in.decodeError(s"invalid type ${discriminator} found")
}
}
override def encodeValue(x: WebCreateAssetAttribute, out: JsonWriter): Unit = x match {
case v: WebCreateStringAssetAttribute => stringAssetAttributeCodec.encodeValue(v, out)
case v: WebCreateDataTableAssetAttribute => dataTableAssetAttributeCodec.encodeValue(v, out)
}
override def nullValue: WebCreateAssetAttribute = null.asInstanceOf[WebCreateAssetAttribute]
}
implicit val codec: JsonValueCodec[Either[String, Long]] = new JsonValueCodec[Either[String, Long]] {
override def decodeValue(in: JsonReader, default: Either[String, Long]): Either[String, Long] = {
if(in.isNextToken('"')) Left(in.readString(""))
else Right(in.readLong())
}
override def encodeValue(x: Either[String, Long], out: JsonWriter): Unit =
x match {
case Left(value) => out.writeVal(value)
case Right(value) => out.writeVal(value)
}
override def nullValue: Either[String, Long] = null
}
implicit val eitherCodec: JsonValueCodec[Either[String, Long]] =
new JsonValueCodec[Either[String, Long]] {
override def decodeValue(
in: JsonReader,
default: Either[String, Long]
): Either[String, Long] = {
val rawString = new String(in.readRawValAsBytes())
if (rawString.head == '"') Left(rawString.drop(1).dropRight(1))
else
Right(
rawString.toLongOption match {
case Some(value) => value
case None =>
throw new Exception(
s"Parse error: $rawString is not a string or number"
)
}
)
}
override def encodeValue(x: Either[String, Long], out: JsonWriter): Unit =
x match {
case Left(value) => out.writeVal(value)
case Right(value) => out.writeVal(value)
}
override def nullValue: Either[String, Long] = null
}
Int
by Long
in that snippet to have a codec for Either[String, Long]
.
Hello! I have this scenario where i am parsing a json with a datetime field that jsoniter cannot parse with the default codec. What is the most ergonomic way to parse that field with a specific datetimeformatter?
I have a case class that looks something like this:
case class Foo(
bar: String,
time: Option[LocalDateTime]
)
The json looks something like this:
{
"bar": "foo",
"time": "02/02/2022 11:22"
}
Jsoniter throws this error with the default codec: Caused by: com.github.plokhotnyuk.jsoniter_scala.core.JsonReaderException: expected digit, offset: 0x00000fb8, buf:
Thanks!
LocalDateTime
from/to the provided format and how to inject it using implicit val
for derivation of a codec that parses an array of such values: plokhotnyuk/jsoniter-scala@748e149
String
instantiation and reparsing through DateTimeFormatter
.
Thanks, that is very helpful. I have a follow up question. How do i scope that codec to only be used within a specific case class, or even better only on a specific field? I am parsing a structure of several nested case classes, and several of them have fields of the type LocalDateTime, but I only want to use this codec on one of them.
For example:
case class FruitStore(
bar: String,
opensAt: Option[LocalDateTime], // Uses default codec
closesAt: Option[LocalDateTime], // Uses default codec
fruits: Seq[Fruit]
)
case class Fruit(
bar: String,
expiresAt: Option[LocalDateTime] // This field needs to use the special codec
)
val customCodecOfLocalDateTime: JsonValueCodec[LocalDateTime] =
new JsonValueCodec[LocalDateTime] {
private val formatter =
DateTimeFormatter.ofPattern("dd/MM/uuuu HH:mm")
val nullValue: LocalDateTime = null
def decodeValue(in: JsonReader, default: LocalDateTime): LocalDateTime =
LocalDateTime.parse(in.readString(null), formatter)
def encodeValue(x: LocalDateTime, out: JsonWriter): _root_.scala.Unit =
out.writeNonEscapedAsciiVal(formatter.format(x))
}
// this scope works
implicit val overrideCodecOfLocalDateTime = customCodecOfLocalDateTime
implicit val fruitCodec: JsonValueCodec[Fruit] = {
// this scope gives a compiler error: "local val overrideCodecOfLocalDateTime in value fruitCodec is never used"
// implicit val overrideCodecOfLocalDateTime = customCodecOfLocalDateTime
JsonCodecMaker.make[Fruit]
}
@nowarn
annotation or some Fruit
object definition like here: plokhotnyuk/jsoniter-scala@68b56db