Skip to content

Commit 52521ee

Browse files
authored
support Jackson StreamWriteConstraints (#1206)
1 parent 888cb65 commit 52521ee

File tree

3 files changed

+66
-5
lines changed

3 files changed

+66
-5
lines changed

play-json/jvm/src/main/scala/play/api/libs/json/JsonConfig.scala

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package play.api.libs.json
66

77
import com.fasterxml.jackson.core.StreamReadConstraints
8+
import com.fasterxml.jackson.core.StreamWriteConstraints
89

910
import play.api.libs.json.JsonConfig.defaultMaxPlain
1011
import play.api.libs.json.JsonConfig.defaultMinPlain
@@ -106,6 +107,7 @@ sealed trait JsonConfig {
106107
def bigDecimalParseConfig: BigDecimalParseConfig
107108
def bigDecimalSerializerConfig: BigDecimalSerializerConfig
108109
def streamReadConstraints: StreamReadConstraints
110+
def streamWriteConstraints: StreamWriteConstraints
109111
}
110112

111113
object JsonConfig {
@@ -176,6 +178,11 @@ object JsonConfig {
176178
*/
177179
val maxNestingDepth: String = "play.json.parser.maxNestingDepth"
178180

181+
/**
182+
* The system property to override the max nesting depth for JSON serialization.
183+
*/
184+
val maxSerializerNestingDepth: String = "play.json.serializer.maxNestingDepth"
185+
179186
/**
180187
* The system property to override the max string length for JSON parsing.
181188
* This is used to limit the length of individual strings in JSON documents.
@@ -200,6 +207,9 @@ object JsonConfig {
200207
private[json] def loadMaxNestingDepth: Int =
201208
prop(maxNestingDepth, StreamReadConstraints.DEFAULT_MAX_DEPTH)(Integer.parseInt)
202209

210+
private[json] def loadMaxSerializerNestingDepth: Int =
211+
prop(maxSerializerNestingDepth, StreamWriteConstraints.DEFAULT_MAX_DEPTH)(Integer.parseInt)
212+
203213
private[json] def loadMaxStringLength: Int =
204214
prop(maxStringLength, StreamReadConstraints.DEFAULT_MAX_STRING_LEN)(Integer.parseInt)
205215

@@ -214,13 +224,20 @@ object JsonConfig {
214224
.maxNumberLength(Int.MaxValue) // play-json has its own support for limiting number length
215225
.build()
216226

227+
private[json] val defaultStreamWriteConstraints: StreamWriteConstraints =
228+
StreamWriteConstraints
229+
.builder()
230+
.maxNestingDepth(loadMaxSerializerNestingDepth)
231+
.build()
232+
217233
// Default settings, which can be controlled with system properties.
218234
// To override, call JacksonJson.setConfig()
219235
val settings: JsonConfig =
220236
JsonConfig(
221237
BigDecimalParseConfig(loadMathContext, loadScaleLimit, loadDigitsLimit),
222238
BigDecimalSerializerConfig(loadMinPlain, loadMaxPlain, loadPreserveZeroDecimal),
223-
defaultStreamReadConstraints
239+
defaultStreamReadConstraints,
240+
defaultStreamWriteConstraints
224241
)
225242

226243
def apply(): JsonConfig = apply(BigDecimalParseConfig(), BigDecimalSerializerConfig())
@@ -229,14 +246,20 @@ object JsonConfig {
229246
bigDecimalParseConfig: BigDecimalParseConfig,
230247
bigDecimalSerializerConfig: BigDecimalSerializerConfig
231248
): JsonConfig =
232-
JsonConfigImpl(bigDecimalParseConfig, bigDecimalSerializerConfig, defaultStreamReadConstraints)
249+
JsonConfigImpl(
250+
bigDecimalParseConfig,
251+
bigDecimalSerializerConfig,
252+
defaultStreamReadConstraints,
253+
defaultStreamWriteConstraints
254+
)
233255

234256
def apply(
235257
bigDecimalParseConfig: BigDecimalParseConfig,
236258
bigDecimalSerializerConfig: BigDecimalSerializerConfig,
237-
streamReadConstraints: StreamReadConstraints
259+
streamReadConstraints: StreamReadConstraints,
260+
streamWriteConstraints: StreamWriteConstraints
238261
): JsonConfig =
239-
JsonConfigImpl(bigDecimalParseConfig, bigDecimalSerializerConfig, streamReadConstraints)
262+
JsonConfigImpl(bigDecimalParseConfig, bigDecimalSerializerConfig, streamReadConstraints, streamWriteConstraints)
240263

241264
private[json] def parseMathContext(key: String): MathContext = sys.props.get(key).map(_.toLowerCase) match {
242265
case Some("decimal128") => MathContext.DECIMAL128
@@ -257,5 +280,6 @@ object JsonConfig {
257280
private final case class JsonConfigImpl(
258281
bigDecimalParseConfig: BigDecimalParseConfig,
259282
bigDecimalSerializerConfig: BigDecimalSerializerConfig,
260-
streamReadConstraints: StreamReadConstraints
283+
streamReadConstraints: StreamReadConstraints,
284+
streamWriteConstraints: StreamWriteConstraints
261285
) extends JsonConfig

play-json/jvm/src/main/scala/play/api/libs/json/jackson/JacksonJson.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ private[json] object JacksonJson {
279279
private[play] case class JacksonJson(jsonConfig: JsonConfig) {
280280
private val jsonFactory = new JsonFactoryBuilder()
281281
.streamReadConstraints(jsonConfig.streamReadConstraints)
282+
.streamWriteConstraints(jsonConfig.streamWriteConstraints)
282283
.build()
283284
private val mapper = JsonMapper
284285
.builder(jsonFactory)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
3+
*/
4+
5+
package play.api.libs.json
6+
7+
import com.fasterxml.jackson.core.{ StreamReadConstraints, StreamWriteConstraints }
8+
import org.scalatest.matchers.must.Matchers
9+
import org.scalatest.wordspec.AnyWordSpec
10+
11+
class JsonConfigSpec extends AnyWordSpec with Matchers {
12+
"JsonConfig" should {
13+
"fetch default nesting depth (parsing)" in {
14+
JsonConfig.defaultStreamReadConstraints.getMaxNestingDepth.mustEqual(StreamReadConstraints.DEFAULT_MAX_DEPTH)
15+
}
16+
"fetch default nesting depth (serializer)" in {
17+
JsonConfig.defaultStreamWriteConstraints.getMaxNestingDepth.mustEqual(StreamWriteConstraints.DEFAULT_MAX_DEPTH)
18+
}
19+
"override nesting depth (parsing)" in {
20+
System.setProperty(JsonConfig.maxNestingDepth, "200")
21+
try {
22+
JsonConfig.loadMaxNestingDepth.mustEqual(200)
23+
} finally {
24+
System.clearProperty(JsonConfig.maxNestingDepth)
25+
}
26+
}
27+
"override nesting depth (serializer)" in {
28+
System.setProperty(JsonConfig.maxSerializerNestingDepth, "300")
29+
try {
30+
JsonConfig.loadMaxSerializerNestingDepth.mustEqual(300)
31+
} finally {
32+
System.clearProperty(JsonConfig.maxSerializerNestingDepth)
33+
}
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)