question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Reading numbers (and other types) as string

See original GitHub issue

Currently parsing strings to numbers can be enabled by isStringified or @stringified, but the possibility to enable parsing any value as a String (be it numbers or booleans, etc) could be useful. One case is when parsing json documents from MongoDB where there is no schema on write and users add ids as strings, doubles, longs (for the same field, e.g. id) only to be truncated (if needed) to integers in the end.

To the extent of my knowledge in jsoniter-scala there is currently no possibility to do this without writing custom codec and it takes a lot of coding on user’s side.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:1
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
plokhotnyukcommented, Jan 14, 2020

Lets for simplicity assume that 64 bits will be enough for internal binary representation of ID.

Than the definition of the ID type and a codec for it can be like in the code snippet below:

import com.github.plokhotnyuk.jsoniter_scala.macros._
import com.github.plokhotnyuk.jsoniter_scala.core._

case class ID(bits: Long) {
  override def hashCode: Int = (bits ^ (bits >> 32)).toInt

  override def equals(obj: Any): _root_.scala.Boolean = obj match {
    case that: ID => bits == that.bits
    case _ => false
  }

  override def toString: String = bits.toString
}

object ID {
  implicit val codec: JsonValueCodec[ID] = new JsonValueCodec[ID] {
    override def decodeValue(in: JsonReader, default: ID): ID = new ID({
      in.nextToken() match {
        case '{' =>
          val n = in.readKeyAsCharBuf()
          var l = 0L
          if (!in.isCharBufEqualsTo(n, "$numberLong") || {
            l = in.readStringAsLong()
            !in.isNextToken('}')
          }) in.decodeError("illegal ID value")
          l
        case '"' =>
          in.rollbackToken()
          val d = in.readStringAsDouble()
          if (d != d.toLong) in.decodeError("illegal ID value")
          d.toLong
        case _ =>
          in.rollbackToken()
          val d = in.readDouble()
          if (d != d.toLong) in.decodeError("illegal ID value")
          d.toLong
      }
    })

    override def encodeValue(x: ID, out: JsonWriter): Unit = {
      val bits = x.bits
      if (bits != bits.toDouble.toLong) {
        out.writeObjectStart()
        out.writeNonEscapedAsciiKey("$numberLong")
        out.writeValAsString(bits)
        out.writeObjectEnd()
      } else out.writeVal(bits)
    }

    override val nullValue: ID = new ID(0L)
  }
}

case class User(id: ID, name: String)

implicit val codec: JsonValueCodec[User] = JsonCodecMaker.make(CodecMakerConfig)

val jsonSamples = Seq(
  """{"id":{"$numberLong": "123456789" }, "name": "Dave"}""",
  """{"id":"123456789", "name": "Dave"}""",
  """{"id":123456789, "name": "Dave"}""",
  """{"id":123456789.0, "name": "Dave"}"""
).map(_.getBytes("UTF-8"))

jsonSamples.foreach(json => println(readFromArray[User](json)))

println(new String(writeToArray(User(id = ID(1234567890123456789L), name = "John")), "UTF-8"))
println(new String(writeToArray(User(id = ID(123456789L), name = "Dave")), "UTF-8"))

It should print the following output:

User(123456789,Dave)
User(123456789,Dave)
User(123456789,Dave)
User(123456789,Dave)
{"id":{"$numberLong":"1234567890123456789"},"name":"John"}
{"id":123456789,"name":"Dave"}
0reactions
hubert-skowronekcommented, Jan 14, 2020

Do you have some example of implementation of such custom codec?

Any other options for representation of IDs in Mongo is rather everything - Longs, Strings, UUIDs, compound types, etc (but in my case id is a number but can be persisted as int/long/double/stringified number…)

You are right, the following representations are equal in my case.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Data Types in Programming: Numbers, Strings and Others
One of the most widely used data types is a string. A string consists of one or more characters, which can include letters,...
Read more >
Conversions Between Strings and Other Types - Visual Basic
Conversion of Numbers to Strings​​ Format automatically uses the appropriate symbols according to the Regional Options settings specified in the ...
Read more >
10. Numbers, Characters, and Strings - gigamonkeys
All rational numbers are "canonicalized" as they're read--that's why 10 and 20/2 are both read as the same number, as are 3/4 and...
Read more >
How to read number as a String type and return an output in ...
I'd use enum like this: public enum MyNumber { ONE(1), TWO(2), THREE(3); private final int value; MyNumber(int value){ this.value = value; } ...
Read more >
Data Types and Type Conversion
How can I convert one type to another? Objectives. Explain key differences between ... Explain key differences between numbers and character strings.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found