Skip to content

Commit

Permalink
avoid reparsing numbers when serializing
Browse files Browse the repository at this point in the history
Update StringBasedNumericNode.scala

rework

Update JacksonJson.scala

add containsEOrDot
  • Loading branch information
pjfanning committed Sep 5, 2024
1 parent 6d2feae commit d97f4dd
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package play.api.libs.json.jackson

import java.io.InputStream
import java.io.StringWriter
import java.math.BigInteger

import scala.annotation.switch
import scala.annotation.tailrec
Expand All @@ -26,6 +27,7 @@ import com.fasterxml.jackson.databind._
import com.fasterxml.jackson.databind.`type`.TypeFactory
import com.fasterxml.jackson.databind.deser.Deserializers
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.node.BigIntegerNode
import com.fasterxml.jackson.databind.ser.Serializers

import play.api.libs.json._
Expand Down Expand Up @@ -67,12 +69,8 @@ sealed class PlayJsonMapperModule(jsonConfig: JsonConfig) extends SimpleModule("
// -- Serializers.

private[jackson] class JsValueSerializer(jsonConfig: JsonConfig) extends JsonSerializer[JsValue] {
import java.math.BigInteger
import java.math.{ BigDecimal => JBigDec }

import com.fasterxml.jackson.databind.node.BigIntegerNode
import com.fasterxml.jackson.databind.node.DecimalNode

private def stripTrailingZeros(bigDec: JBigDec): JBigDec = {
val stripped = bigDec.stripTrailingZeros
if (jsonConfig.bigDecimalSerializerConfig.preserveZeroDecimal && bigDec.scale > 0 && stripped.scale <= 0) {
Expand All @@ -96,10 +94,10 @@ private[jackson] class JsValueSerializer(jsonConfig: JsonConfig) extends JsonSer
val stripped = stripTrailingZeros(v.bigDecimal)
val raw = if (shouldWritePlain) stripped.toPlainString else stripped.toString

if (raw.indexOf('E') < 0 && raw.indexOf('.') < 0)
json.writeTree(new BigIntegerNode(new BigInteger(raw)))
if (containsEOrDot(raw))
json.writeTree(StringBasedNumericNode(raw))
else
json.writeTree(new DecimalNode(new JBigDec(raw)))
json.writeTree(new BigIntegerNode(new BigInteger(raw)))
}

case JsString(v) => json.writeString(v)
Expand All @@ -125,6 +123,16 @@ private[jackson] class JsValueSerializer(jsonConfig: JsonConfig) extends JsonSer
case JsNull => json.writeNull()
}
}

private def containsEOrDot(s: String): Boolean = {
var i = 0
while (i < s.length) {
val c = s.charAt(i)
if (c == 'E' || c == '.') return true
i += 1
}
false
}
}

private[jackson] sealed trait DeserializerContext {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
*/

package play.api.libs.json.jackson

import com.fasterxml.jackson.core.{ JsonGenerator, JsonParser, JsonToken }
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.node.NumericNode

import java.math.{ BigDecimal, BigInteger }

/**
* A numeric node that is represented as a string.
*
* For internal use only. Some methods are not implemented and will throw an exception.
*/
private[jackson] case class StringBasedNumericNode(text: String) extends NumericNode {

override def numberType: JsonParser.NumberType = JsonParser.NumberType.BIG_DECIMAL

override def asText(): String = text

override def asToken(): JsonToken = JsonToken.VALUE_NUMBER_FLOAT

override def isIntegralNumber: Boolean = false

override def serialize(jgen: JsonGenerator, ctxt: SerializerProvider): Unit =
jgen.writeNumber(text)

override def numberValue: Number = throw new NotImplementedError

override def intValue: Int = throw new NotImplementedError

override def longValue: Long = throw new NotImplementedError

override def doubleValue: Double = throw new NotImplementedError

override def decimalValue: BigDecimal = throw new NotImplementedError

override def bigIntegerValue: BigInteger = throw new NotImplementedError

override def canConvertToInt: Boolean = false

override def canConvertToLong: Boolean = false
}

0 comments on commit d97f4dd

Please sign in to comment.