From 6231dd3c5fee95ce881566ffbe39a4ce81dafb92 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 5 Jan 2025 17:59:34 +0100 Subject: [PATCH 01/15] Update build-test.yml --- .github/workflows/build-test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 3490cc6da..5fd83a9f0 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -1,6 +1,7 @@ name: Check on: + workflow_dispatch: pull_request: push: From e24c1ca922ef9bc32f9370aa9e013fe92c4f1371 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 5 Jan 2025 19:23:02 +0100 Subject: [PATCH 02/15] change pom --- project/Common.scala | 26 +++++++++++++++++--------- project/build.properties | 2 +- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/project/Common.scala b/project/Common.scala index c1afdc084..5eacd8879 100644 --- a/project/Common.scala +++ b/project/Common.scala @@ -11,19 +11,27 @@ object Common extends AutoPlugin { override def globalSettings = Seq( - organization := "org.playframework", + organization := "com.github.pjfanning", organizationName := "The Play Framework Project", - organizationHomepage := Some(url("https://playframework.com/")), - homepage := Some(url(s"https://github.com/playframework/${repoName}")), + organizationHomepage := Some(url("https://github.com/pjfanning")), + homepage := Some(url(s"https://github.com/pjfanning/${repoName}")), licenses := Seq("Apache-2.0" -> url("https://www.apache.org/licenses/LICENSE-2.0.html")), scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked", "-encoding", "utf8"), javacOptions ++= Seq("-encoding", "UTF-8", "-Xlint:-options"), - developers += Developer( - "playframework", - "The Play Framework Contributors", - "contact@playframework.com", - url("https://github.com/playframework") + developers ++= Seq( + Developer( + "playframework", + "The Play Framework Contributors", + "contact@playframework.com", + url("https://github.com/playframework") + ), + Developer( + "pjfanning", + "PJ Fanning", + "", + url("https://github.com/pjfanning") + ) ), - description := "Play JSON" + description := "Play JSON (fork)" ) } diff --git a/project/build.properties b/project/build.properties index db1723b08..73df629ac 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.5 +sbt.version=1.10.7 From 4f93df05e63b60bbff7087db7dd9aab99e2f120c Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 5 Jan 2025 19:29:12 +0100 Subject: [PATCH 03/15] Update Common.scala --- project/Common.scala | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/project/Common.scala b/project/Common.scala index 5eacd8879..032e62af6 100644 --- a/project/Common.scala +++ b/project/Common.scala @@ -18,19 +18,17 @@ object Common extends AutoPlugin { licenses := Seq("Apache-2.0" -> url("https://www.apache.org/licenses/LICENSE-2.0.html")), scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked", "-encoding", "utf8"), javacOptions ++= Seq("-encoding", "UTF-8", "-Xlint:-options"), - developers ++= Seq( - Developer( - "playframework", - "The Play Framework Contributors", - "contact@playframework.com", - url("https://github.com/playframework") - ), - Developer( - "pjfanning", - "PJ Fanning", - "", - url("https://github.com/pjfanning") - ) + developers += Developer( + "playframework", + "The Play Framework Contributors", + "contact@playframework.com", + url("https://github.com/playframework") + ), + developers += Developer( + "pjfanning", + "PJ Fanning", + "", + url("https://github.com/pjfanning") ), description := "Play JSON (fork)" ) From e0bf6a49816da47b887acda411da2f55719de583 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 5 Jan 2025 19:33:38 +0100 Subject: [PATCH 04/15] rework CI --- .github/workflows/build-test.yml | 6 +++--- .github/workflows/release-drafter.yml | 18 ------------------ 2 files changed, 3 insertions(+), 21 deletions(-) delete mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 5fd83a9f0..d94ac2f9e 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -20,9 +20,9 @@ jobs: with: cmd: sbt validateCode - check-binary-compatibility: - name: Binary Compatibility - uses: playframework/.github/.github/workflows/binary-check.yml@v4 + #check-binary-compatibility: + # name: Binary Compatibility + # uses: playframework/.github/.github/workflows/binary-check.yml@v4 check-docs: name: Docs diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml deleted file mode 100644 index a9f76d5da..000000000 --- a/.github/workflows/release-drafter.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Release Drafter - -on: - push: - branches: - - main - -jobs: - update_release_draft: - runs-on: ubuntu-latest - steps: - - uses: release-drafter/release-drafter@v6 - with: - name: "Play JSON $RESOLVED_VERSION" - config-name: release-drafts/increasing-minor-version.yml # located in .github/ in the default branch within this or the .github repo - commitish: ${{ github.ref_name }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From f56bd12d632b1bdd49e76d1ca963d0d704c99fd1 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 5 Jan 2025 19:44:29 +0100 Subject: [PATCH 05/15] Update build-test.yml --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index d94ac2f9e..d0569987e 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -34,8 +34,8 @@ jobs: name: Tests needs: - "check-code-style" - - "check-binary-compatibility" - "check-docs" + # - "check-binary-compatibility" uses: playframework/.github/.github/workflows/cmd.yml@v4 with: java: 21, 17 From 94bb6615897e57a0da542cff2764f84684b70b2c Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 5 Sep 2024 01:44:58 +0100 Subject: [PATCH 06/15] support jackson 2.17 more changes Create PR1072.backwards.excludes Update JsonConfig.scala Update JacksonJson.scala --- build.sbt | 2 +- .../PR1072.backwards.excludes | 2 ++ .../scala/play/api/libs/json/JsonConfig.scala | 36 ++++++++++++++++--- .../api/libs/json/jackson/JacksonJson.scala | 19 +++++----- 4 files changed, 45 insertions(+), 14 deletions(-) create mode 100644 play-json/jvm/src/main/mima-filters/3.1.x.backwards.excludes/PR1072.backwards.excludes diff --git a/build.sbt b/build.sbt index 223fcb45a..cd71eb01f 100644 --- a/build.sbt +++ b/build.sbt @@ -21,7 +21,7 @@ def specs2(scalaVersion: String) = ("org.specs2" %% s"specs2-$n" % "4.20.9") % Test } -val jacksonDatabindVersion = "2.14.3" +val jacksonDatabindVersion = "2.17.2" val jacksonDatabind = Seq( "com.fasterxml.jackson.core" % "jackson-databind" % jacksonDatabindVersion ) diff --git a/play-json/jvm/src/main/mima-filters/3.1.x.backwards.excludes/PR1072.backwards.excludes b/play-json/jvm/src/main/mima-filters/3.1.x.backwards.excludes/PR1072.backwards.excludes new file mode 100644 index 000000000..b417d126f --- /dev/null +++ b/play-json/jvm/src/main/mima-filters/3.1.x.backwards.excludes/PR1072.backwards.excludes @@ -0,0 +1,2 @@ +# private final class JsonConfigImpl has changed +ProblemFilters.exclude[MissingTypesProblem]("play.api.libs.json.JsonConfigImpl$") diff --git a/play-json/jvm/src/main/scala/play/api/libs/json/JsonConfig.scala b/play-json/jvm/src/main/scala/play/api/libs/json/JsonConfig.scala index 71d21d963..a25dd3a3f 100644 --- a/play-json/jvm/src/main/scala/play/api/libs/json/JsonConfig.scala +++ b/play-json/jvm/src/main/scala/play/api/libs/json/JsonConfig.scala @@ -4,6 +4,8 @@ package play.api.libs.json +import com.fasterxml.jackson.core.StreamReadConstraints + import play.api.libs.json.JsonConfig.defaultMaxPlain import play.api.libs.json.JsonConfig.defaultMinPlain import play.api.libs.json.JsonConfig.defaultDigitsLimit @@ -103,6 +105,7 @@ private final case class DecimalSerializerSettingsImpl( sealed trait JsonConfig { def bigDecimalParseConfig: BigDecimalParseConfig def bigDecimalSerializerConfig: BigDecimalSerializerConfig + def streamReadConstraints: StreamReadConstraints } object JsonConfig { @@ -168,6 +171,11 @@ object JsonConfig { */ val maxPlainProperty: String = "play.json.serializer.maxPlain" + /** + * The system property to override the max nesting depth for JSON parsing. + */ + val maxNestingDepth: String = "play.json.parser.maxNestingDepth" + /** * The system property to override whether zero decimals (e.g. .0 or .00) are written by default. These are dropped by default. */ @@ -183,15 +191,26 @@ object JsonConfig { private[json] def loadMaxPlain: BigDecimal = prop(maxPlainProperty, defaultMaxPlain)(BigDecimal.exact) + private[json] def loadMaxNestingDepth: Int = + prop(maxNestingDepth, StreamReadConstraints.DEFAULT_MAX_DEPTH)(Integer.parseInt) + private[json] def loadPreserveZeroDecimal: Boolean = prop(preserveZeroDecimalProperty, defaultPreserveZeroDecimal)(_.toBoolean) + private[json] val defaultStreamReadConstraints: StreamReadConstraints = + StreamReadConstraints + .builder() + .maxNestingDepth(loadMaxNestingDepth) + .maxNumberLength(Int.MaxValue) // play-json has its own support for limiting number length + .build() + // Default settings, which can be controlled with system properties. // To override, call JacksonJson.setConfig() val settings: JsonConfig = JsonConfig( BigDecimalParseConfig(loadMathContext, loadScaleLimit, loadDigitsLimit), - BigDecimalSerializerConfig(loadMinPlain, loadMaxPlain, loadPreserveZeroDecimal) + BigDecimalSerializerConfig(loadMinPlain, loadMaxPlain, loadPreserveZeroDecimal), + defaultStreamReadConstraints ) def apply(): JsonConfig = apply(BigDecimalParseConfig(), BigDecimalSerializerConfig()) @@ -200,7 +219,14 @@ object JsonConfig { bigDecimalParseConfig: BigDecimalParseConfig, bigDecimalSerializerConfig: BigDecimalSerializerConfig ): JsonConfig = - JsonConfigImpl(bigDecimalParseConfig, bigDecimalSerializerConfig) + JsonConfigImpl(bigDecimalParseConfig, bigDecimalSerializerConfig, defaultStreamReadConstraints) + + def apply( + bigDecimalParseConfig: BigDecimalParseConfig, + bigDecimalSerializerConfig: BigDecimalSerializerConfig, + streamReadConstraints: StreamReadConstraints + ): JsonConfig = + JsonConfigImpl(bigDecimalParseConfig, bigDecimalSerializerConfig, streamReadConstraints) private[json] def parseMathContext(key: String): MathContext = sys.props.get(key).map(_.toLowerCase) match { case Some("decimal128") => MathContext.DECIMAL128 @@ -220,7 +246,8 @@ object JsonConfig { private final case class JsonConfigImpl( bigDecimalParseConfig: BigDecimalParseConfig, - bigDecimalSerializerConfig: BigDecimalSerializerConfig + bigDecimalSerializerConfig: BigDecimalSerializerConfig, + streamReadConstraints: StreamReadConstraints ) extends JsonConfig @deprecated("Use BigDecimalParseConfig instead", "2.9.4") @@ -241,7 +268,8 @@ final case class BigDecimalSerializerSettings( @deprecated("Use JsonConfig instead", "2.9.4") final case class JsonParserSettings( bigDecimalParseSettings: BigDecimalParseSettings, - bigDecimalSerializerSettings: BigDecimalSerializerSettings + bigDecimalSerializerSettings: BigDecimalSerializerSettings, + streamReadConstraints: StreamReadConstraints = JsonConfig.defaultStreamReadConstraints ) extends JsonConfig { override def bigDecimalParseConfig: BigDecimalParseConfig = bigDecimalParseSettings diff --git a/play-json/jvm/src/main/scala/play/api/libs/json/jackson/JacksonJson.scala b/play-json/jvm/src/main/scala/play/api/libs/json/jackson/JacksonJson.scala index 44f0c090d..a0a54211f 100644 --- a/play-json/jvm/src/main/scala/play/api/libs/json/jackson/JacksonJson.scala +++ b/play-json/jvm/src/main/scala/play/api/libs/json/jackson/JacksonJson.scala @@ -13,11 +13,7 @@ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ListBuffer -import com.fasterxml.jackson.core.JsonFactory -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.core.JsonTokenId -import com.fasterxml.jackson.core.Version +import com.fasterxml.jackson.core.{ JsonFactoryBuilder, JsonGenerator, JsonParser, JsonTokenId, Version } import com.fasterxml.jackson.core.json.JsonWriteFeature import com.fasterxml.jackson.core.util.DefaultPrettyPrinter @@ -25,6 +21,7 @@ import com.fasterxml.jackson.databind.Module.SetupContext import com.fasterxml.jackson.databind._ import com.fasterxml.jackson.databind.`type`.TypeFactory import com.fasterxml.jackson.databind.deser.Deserializers +import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.databind.ser.Serializers @@ -219,7 +216,7 @@ private[jackson] class JsValueDeserializer(factory: TypeFactory, klass: Class[_] case JsonTokenId.ID_FIELD_NAME => parserContext match { - case (c: ReadingMap) :: stack => (None, c.setField(jp.getCurrentName) +: stack) + case (c: ReadingMap) :: stack => (None, c.setField(jp.currentName()) +: stack) case _ => throw new RuntimeException("We should be reading map, something got wrong") } @@ -282,9 +279,13 @@ private[json] object JacksonJson { } private[json] case class JacksonJson(jsonConfig: JsonConfig) { - private val mapper = (new ObjectMapper).registerModule(new PlayJsonMapperModule(jsonConfig)) - - private val jsonFactory = new JsonFactory(mapper) + private val jsonFactory = new JsonFactoryBuilder() + .streamReadConstraints(jsonConfig.streamReadConstraints) + .build() + private val mapper = JsonMapper + .builder(jsonFactory) + .addModule(new PlayJsonMapperModule(jsonConfig)) + .build() private def stringJsonGenerator(out: java.io.StringWriter) = jsonFactory.createGenerator(out) From 2ce9bc76bc161652da0ef493e4c594100fd810d8 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 3 Jan 2025 14:33:34 +0100 Subject: [PATCH 07/15] Update build.sbt --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index cd71eb01f..7b2630cf9 100644 --- a/build.sbt +++ b/build.sbt @@ -21,7 +21,7 @@ def specs2(scalaVersion: String) = ("org.specs2" %% s"specs2-$n" % "4.20.9") % Test } -val jacksonDatabindVersion = "2.17.2" +val jacksonDatabindVersion = "2.17.3" val jacksonDatabind = Seq( "com.fasterxml.jackson.core" % "jackson-databind" % jacksonDatabindVersion ) From deaa603f8619472dba0a5a16af4ea697f22171a6 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 5 Sep 2024 04:18:08 +0100 Subject: [PATCH 08/15] avoid reparsing numbers when serializing Update StringBasedNumericNode.scala rework Update JacksonJson.scala add containsEOrDot requested changes to containsEOrDot remove StringBasedNumericNode imports remove new function add test add int serialization test Update JsonSpec.scala Update JsonSpec.scala --- .../api/libs/json/jackson/JacksonJson.scala | 18 ++++--- .../scala/play/api/libs/json/JsonSpec.scala | 50 +++++++++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/play-json/jvm/src/main/scala/play/api/libs/json/jackson/JacksonJson.scala b/play-json/jvm/src/main/scala/play/api/libs/json/jackson/JacksonJson.scala index a0a54211f..261f0374d 100644 --- a/play-json/jvm/src/main/scala/play/api/libs/json/jackson/JacksonJson.scala +++ b/play-json/jvm/src/main/scala/play/api/libs/json/jackson/JacksonJson.scala @@ -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 @@ -23,7 +24,9 @@ import com.fasterxml.jackson.databind.`type`.TypeFactory import com.fasterxml.jackson.databind.deser.Deserializers import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.node.BigIntegerNode import com.fasterxml.jackson.databind.ser.Serializers +import com.fasterxml.jackson.databind.util.TokenBuffer import play.api.libs.json._ @@ -64,12 +67,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) { @@ -93,10 +92,15 @@ 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 (raw.exists(c => c == 'E' || c == '.')) + json.writeNumber(raw) else - json.writeTree(new DecimalNode(new JBigDec(raw))) + json match { + case _: TokenBuffer => + json.writeTree(new BigIntegerNode(new BigInteger(raw))) + case _ => + json.writeNumber(raw) + } } case JsString(v) => json.writeString(v) diff --git a/play-json/jvm/src/test/scala/play/api/libs/json/JsonSpec.scala b/play-json/jvm/src/test/scala/play/api/libs/json/JsonSpec.scala index 8411c61ea..03bf83184 100644 --- a/play-json/jvm/src/test/scala/play/api/libs/json/JsonSpec.scala +++ b/play-json/jvm/src/test/scala/play/api/libs/json/JsonSpec.scala @@ -4,6 +4,7 @@ package play.api.libs.json +import java.math.BigInteger import java.util.Calendar import java.util.Date import java.util.TimeZone @@ -477,6 +478,55 @@ class JsonSpec extends org.specs2.mutable.Specification { fromJson[JsonNode](jsNum).map(_.toString).must_==(JsSuccess("12.345")) } + "Serialize JsNumbers with integers correctly" in { + val numStrings = Seq( + "0", + "1", + "-1", + Int.MaxValue.toString, + Int.MinValue.toString, + Long.MaxValue.toString, + Long.MinValue.toString, + BigInteger.valueOf(Long.MaxValue).add(BigInteger.ONE).toString, + BigInteger.valueOf(Long.MinValue).add(BigInteger.valueOf(-1)).toString + ) + numStrings.map { numString => + val bigDec = new java.math.BigDecimal(numString) + Json.stringify(JsNumber(bigDec)).must_==(bigDec.toString) + } + } + + "Serialize JsNumbers with decimal points correctly" in { + val numStrings = Seq( + "0.123", + "1.23456789", + "-1.23456789", + Float.MaxValue.toString, + Float.MinValue.toString, + Double.MaxValue.toString, + Double.MinValue.toString, + java.math.BigDecimal.valueOf(Double.MaxValue).add(java.math.BigDecimal.valueOf(1)).toString, + java.math.BigDecimal.valueOf(Double.MinValue).add(java.math.BigDecimal.valueOf(-1)).toString + ) + numStrings.map { numString => + val bigDec = new java.math.BigDecimal(numString) + Json.stringify(JsNumber(bigDec)).must_==(bigDec.toString) + } + } + + "Serialize JsNumbers with e notation correctly" in { + val numStrings = Seq( + "1.23456789012345679012345679e999", + "-1.23456789012345679012345679e999", + "1.23456789012345679012345679e-999", + "-1.23456789012345679012345679e-999" + ) + numStrings.map { numString => + val bigDec = new java.math.BigDecimal(numString) + Json.stringify(JsNumber(bigDec)).must_==(bigDec.toString) + } + } + "parse from InputStream" in { val js = Json.obj( "key1" -> "value1", From a85f54dc3991b9b404eebf174a89c7645f3e58a8 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 5 Jan 2025 20:13:51 +0100 Subject: [PATCH 09/15] try to support java 11 --- .github/workflows/build-test.yml | 2 +- build.sbt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index d0569987e..f23d3552d 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -38,7 +38,7 @@ jobs: # - "check-binary-compatibility" uses: playframework/.github/.github/workflows/cmd.yml@v4 with: - java: 21, 17 + java: 21, 17, 11 scala: 2.12.x, 2.13.x, 3.x cmd: scripts/test-code.sh diff --git a/build.sbt b/build.sbt index 7b2630cf9..6b9b975ca 100644 --- a/build.sbt +++ b/build.sbt @@ -65,7 +65,7 @@ def playJsonMimaSettings = Seq( val javacSettings = Seq( "-source", - "17", + "11", "-Xlint:deprecation", "-Xlint:unchecked", ) @@ -73,7 +73,7 @@ val javacSettings = Seq( val scalacOpts = Seq( "-language:higherKinds", "-release", - "17", + "11", "-Ywarn-unused:imports", "-Xlint:nullary-unit", "-Xlint", @@ -110,7 +110,7 @@ lazy val commonSettings = Def.settings( crossScalaVersions := Seq(Dependencies.Scala212, Dependencies.Scala213, Dependencies.Scala3), Compile / javacOptions ++= javacSettings, Test / javacOptions ++= javacSettings, - Compile / compile / javacOptions ++= Seq("--release", "17"), // sbt #1785, avoids passing to javadoc + Compile / compile / javacOptions ++= Seq("--release", "11"), // sbt #1785, avoids passing to javadoc scalacOptions ++= (if (isScala3.value) Nil else scalacOpts), Compile / doc / scalacOptions ++= Seq( // Work around 2.12 bug which prevents javadoc in nested java classes from compiling. From d88331bbcb9ba051b98738f0ce2688a02999e924 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 5 Jan 2025 20:34:56 +0100 Subject: [PATCH 10/15] Update Common.scala --- project/Common.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Common.scala b/project/Common.scala index 032e62af6..e1bafa44e 100644 --- a/project/Common.scala +++ b/project/Common.scala @@ -12,7 +12,7 @@ object Common extends AutoPlugin { override def globalSettings = Seq( organization := "com.github.pjfanning", - organizationName := "The Play Framework Project", + organizationName := "com.github.pjfanning", organizationHomepage := Some(url("https://github.com/pjfanning")), homepage := Some(url(s"https://github.com/pjfanning/${repoName}")), licenses := Seq("Apache-2.0" -> url("https://www.apache.org/licenses/LICENSE-2.0.html")), From db4c76819684e7cb6dc8b1a3f8d43cc7b5750e4b Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 5 Jan 2025 21:36:37 +0100 Subject: [PATCH 11/15] Update build.sbt --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 6b9b975ca..62bc0e836 100644 --- a/build.sbt +++ b/build.sbt @@ -251,7 +251,7 @@ lazy val `play-jsonJVM` = `play-json`.jvm else specs2(scalaVersion.value) } :+ ( - "ch.qos.logback" % "logback-classic" % "1.5.12" % Test + "ch.qos.logback" % "logback-classic" % "1.3.15" % Test ), Test / unmanagedSourceDirectories ++= (docsP / PlayDocsKeys.scalaManualSourceDirectories).value, ) From e30c77419c2d96ae683d71ead5be2d2c2f3649a1 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 5 Jan 2025 21:43:17 +0100 Subject: [PATCH 12/15] Update build.sbt --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 62bc0e836..b8bcc040f 100644 --- a/build.sbt +++ b/build.sbt @@ -26,7 +26,7 @@ val jacksonDatabind = Seq( "com.fasterxml.jackson.core" % "jackson-databind" % jacksonDatabindVersion ) -val jacksonVersion = "2.14.3" +val jacksonVersion = jacksonDatabindVersion val jacksons = Seq( "com.fasterxml.jackson.core" % "jackson-core", "com.fasterxml.jackson.core" % "jackson-annotations", From 42556a738171d0e7efc358a0f667262bee0489a3 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 5 Jan 2025 22:29:01 +0100 Subject: [PATCH 13/15] support overriding max string length (#4) --- .../src/main/scala/play/api/libs/json/JsonConfig.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/play-json/jvm/src/main/scala/play/api/libs/json/JsonConfig.scala b/play-json/jvm/src/main/scala/play/api/libs/json/JsonConfig.scala index a25dd3a3f..9928d404d 100644 --- a/play-json/jvm/src/main/scala/play/api/libs/json/JsonConfig.scala +++ b/play-json/jvm/src/main/scala/play/api/libs/json/JsonConfig.scala @@ -176,6 +176,12 @@ object JsonConfig { */ val maxNestingDepth: String = "play.json.parser.maxNestingDepth" + /** + * The system property to override the max string length for JSON parsing. + * This is used to limit the length of individual strings in JSON documents. + */ + val maxStringLength: String = "play.json.parser.maxStringLength" + /** * The system property to override whether zero decimals (e.g. .0 or .00) are written by default. These are dropped by default. */ @@ -194,6 +200,9 @@ object JsonConfig { private[json] def loadMaxNestingDepth: Int = prop(maxNestingDepth, StreamReadConstraints.DEFAULT_MAX_DEPTH)(Integer.parseInt) + private[json] def loadMaxStringLength: Int = + prop(maxStringLength, StreamReadConstraints.DEFAULT_MAX_STRING_LEN)(Integer.parseInt) + private[json] def loadPreserveZeroDecimal: Boolean = prop(preserveZeroDecimalProperty, defaultPreserveZeroDecimal)(_.toBoolean) @@ -201,6 +210,7 @@ object JsonConfig { StreamReadConstraints .builder() .maxNestingDepth(loadMaxNestingDepth) + .maxStringLength(loadMaxStringLength) .maxNumberLength(Int.MaxValue) // play-json has its own support for limiting number length .build() From 01c6fcde84f65e8e483044e2ffbeb2291a9d9d80 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 6 Jan 2025 19:32:35 +0100 Subject: [PATCH 14/15] try to support typesafe config (#5) * try to support typesafe config * scalafmt --- build.sbt | 3 ++- play-json/jvm/src/main/resources/reference.conf | 10 ++++++++++ .../src/main/scala/play/api/libs/json/JsonConfig.scala | 9 +++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 play-json/jvm/src/main/resources/reference.conf diff --git a/build.sbt b/build.sbt index b8bcc040f..396c91bc5 100644 --- a/build.sbt +++ b/build.sbt @@ -250,7 +250,8 @@ lazy val `play-jsonJVM` = `play-json`.jvm specs2(scalaVersion.value).map(_.exclude("org.scala-lang.modules", "scala-xml_2.13")) else specs2(scalaVersion.value) - } :+ ( + } ++ Seq( + "com.typesafe" % "config" % "1.4.3", "ch.qos.logback" % "logback-classic" % "1.3.15" % Test ), Test / unmanagedSourceDirectories ++= (docsP / PlayDocsKeys.scalaManualSourceDirectories).value, diff --git a/play-json/jvm/src/main/resources/reference.conf b/play-json/jvm/src/main/resources/reference.conf new file mode 100644 index 000000000..e0c6ffce3 --- /dev/null +++ b/play-json/jvm/src/main/resources/reference.conf @@ -0,0 +1,10 @@ +play.json { + jackson { + read { + # see https://www.javadoc.io/static/com.fasterxml.jackson.core/jackson-core/2.17.3/com/fasterxml/jackson/core/StreamReadConstraints.html + # these defaults are the same as the defaults in `StreamReadConstraints` + max-nesting-depth = 1000 + max-string-length = 20000000 + } + } +} diff --git a/play-json/jvm/src/main/scala/play/api/libs/json/JsonConfig.scala b/play-json/jvm/src/main/scala/play/api/libs/json/JsonConfig.scala index 9928d404d..105c49c7b 100644 --- a/play-json/jvm/src/main/scala/play/api/libs/json/JsonConfig.scala +++ b/play-json/jvm/src/main/scala/play/api/libs/json/JsonConfig.scala @@ -5,6 +5,7 @@ package play.api.libs.json import com.fasterxml.jackson.core.StreamReadConstraints +import com.typesafe.config.ConfigFactory import play.api.libs.json.JsonConfig.defaultMaxPlain import play.api.libs.json.JsonConfig.defaultMinPlain @@ -187,6 +188,10 @@ object JsonConfig { */ val preserveZeroDecimalProperty: String = "play.json.serializer.preserveZeroDecimal" + private val playJsonConfig = ConfigFactory + .load() + .getConfig("play.json.jackson") + private[json] def loadScaleLimit: Int = prop(scaleLimitProperty, defaultScaleLimit)(_.toInt) private[json] def loadDigitsLimit: Int = prop(digitsLimitProperty, defaultDigitsLimit)(_.toInt) @@ -198,10 +203,10 @@ object JsonConfig { private[json] def loadMaxPlain: BigDecimal = prop(maxPlainProperty, defaultMaxPlain)(BigDecimal.exact) private[json] def loadMaxNestingDepth: Int = - prop(maxNestingDepth, StreamReadConstraints.DEFAULT_MAX_DEPTH)(Integer.parseInt) + prop(maxNestingDepth, playJsonConfig.getInt("read.max-nesting-depth"))(Integer.parseInt) private[json] def loadMaxStringLength: Int = - prop(maxStringLength, StreamReadConstraints.DEFAULT_MAX_STRING_LEN)(Integer.parseInt) + prop(maxStringLength, playJsonConfig.getInt("read.max-string-length"))(Integer.parseInt) private[json] def loadPreserveZeroDecimal: Boolean = prop(preserveZeroDecimalProperty, defaultPreserveZeroDecimal)(_.toBoolean) From ff7ab786a2edec3c09fa56563aad3056a52dc848 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Wed, 8 Jan 2025 11:15:30 +0100 Subject: [PATCH 15/15] only build play-json for jvm --- build.sbt | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/build.sbt b/build.sbt index 396c91bc5..04da9357b 100644 --- a/build.sbt +++ b/build.sbt @@ -124,22 +124,17 @@ lazy val root = project .enablePlugins(ScalaJSPlugin) .disablePlugins(MimaPlugin) .aggregate( - `play-jsonJS`, - `play-jsonJVM`, - `play-jsonNative`, - `play-functionalJS`, - `play-functionalJVM`, - `play-functionalNative`, - `play-json-joda` + `play-jsonJVM` ) .settings(commonSettings) .settings(publish / skip := true) -lazy val `play-json` = crossProject(JVMPlatform, JSPlatform, NativePlatform) +lazy val `play-json` = crossProject(JVMPlatform) .crossType(CrossType.Full) .in(file("play-json")) .enablePlugins(Omnidoc, Playdoc) .configs(Docs) + /* .jsSettings( libraryDependencies ++= Seq( ("org.scala-js" %%% "scalajs-java-securerandom" % "1.0.0").cross(CrossVersion.for3Use2_13), @@ -150,6 +145,7 @@ lazy val `play-json` = crossProject(JVMPlatform, JSPlatform, NativePlatform) "org.typelevel" %%% "jawn-parser" % "1.6.0" ) ) + */ .settings( commonSettings ++ playJsonMimaSettings ++ Def.settings( libraryDependencies ++= ( @@ -237,10 +233,10 @@ lazy val `play-json` = crossProject(JVMPlatform, JSPlatform, NativePlatform) }.taskValue ) ) - .dependsOn(`play-functional`) +//.dependsOn(`play-functional`) -lazy val `play-jsonJS` = `play-json`.js -lazy val `play-jsonNative` = `play-json`.native +// lazy val `play-jsonJS` = `play-json`.js +// lazy val `play-jsonNative` = `play-json`.native lazy val `play-jsonJVM` = `play-json`.jvm .settings( @@ -251,8 +247,9 @@ lazy val `play-jsonJVM` = `play-json`.jvm else specs2(scalaVersion.value) } ++ Seq( - "com.typesafe" % "config" % "1.4.3", - "ch.qos.logback" % "logback-classic" % "1.3.15" % Test + "org.playframework" %% "play-functional" % "3.0.4", + "com.typesafe" % "config" % "1.4.3", + "ch.qos.logback" % "logback-classic" % "1.3.15" % Test ), Test / unmanagedSourceDirectories ++= (docsP / PlayDocsKeys.scalaManualSourceDirectories).value, ) @@ -264,6 +261,7 @@ def enableJol = Seq( compileOrder := CompileOrder.JavaThenScala, ) +/* lazy val `play-json-joda` = project .in(file("play-json-joda")) .enablePlugins(Omnidoc) @@ -285,6 +283,7 @@ lazy val `play-functional` = crossProject(JVMPlatform, JSPlatform, NativePlatfor lazy val `play-functionalJVM` = `play-functional`.jvm lazy val `play-functionalJS` = `play-functional`.js lazy val `play-functionalNative` = `play-functional`.native + */ lazy val benchmarks = project .in(file("benchmarks"))