Skip to content

Commit 488327d

Browse files
committed
Fix pretty JSON rendering of empty arrays and maps
1 parent 4a8bf46 commit 488327d

File tree

5 files changed

+41
-16
lines changed

5 files changed

+41
-16
lines changed

Diff for: .github/workflows/ci.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
- name: Cache scala dependencies
3333
uses: coursier/cache-action@v6
3434
- name: Lint code
35-
run: sbt ++3.3.4 check
35+
run: sbt ++3.3.5 check
3636

3737
test:
3838
runs-on: ubuntu-24.04
@@ -41,7 +41,7 @@ jobs:
4141
fail-fast: false
4242
matrix:
4343
java: [ '11', '17', '21' ]
44-
scala: [ '3.3.4' ]
44+
scala: [ '3.3.5' ]
4545
platform: [ 'JVM', 'JS' , 'Native' ]
4646
steps:
4747
- name: Checkout current branch

Diff for: core/src/main/scala/io/bullet/borer/Decoder.scala

+8-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@ package io.bullet.borer
1111
import io.bullet.borer.encodings.BaseEncoding
1212
import io.bullet.borer.internal.Util
1313

14-
import java.lang.{Boolean as JBoolean, Byte as JByte, Double as JDouble, Float as JFloat, Long as JLong, Short as JShort}
14+
import java.lang.{
15+
Boolean as JBoolean,
16+
Byte as JByte,
17+
Double as JDouble,
18+
Float as JFloat,
19+
Long as JLong,
20+
Short as JShort
21+
}
1522
import java.math.{BigDecimal as JBigDecimal, BigInteger as JBigInteger}
1623
import scala.annotation.{nowarn, tailrec, threadUnsafe}
1724
import scala.collection.{mutable, Factory}

Diff for: core/src/main/scala/io/bullet/borer/Encoder.scala

+8-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@ package io.bullet.borer
1111
import io.bullet.borer.encodings.BaseEncoding
1212
import io.bullet.borer.internal.{ElementDeque, Util}
1313

14-
import java.lang.{Boolean as JBoolean, Byte as JByte, Double as JDouble, Float as JFloat, Long as JLong, Short as JShort}
14+
import java.lang.{
15+
Boolean as JBoolean,
16+
Byte as JByte,
17+
Double as JDouble,
18+
Float as JFloat,
19+
Long as JLong,
20+
Short as JShort
21+
}
1522
import java.math.{BigDecimal as JBigDecimal, BigInteger as JBigInteger}
1623
import scala.annotation.{tailrec, threadUnsafe}
1724
import scala.collection.LinearSeq

Diff for: core/src/main/scala/io/bullet/borer/json/JsonRenderer.scala

+21-12
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,12 @@ import scala.annotation.tailrec
4646
*/
4747
final private[borer] class JsonRenderer(var out: Output, indent: Int) extends Renderer:
4848

49-
private[this] var level: Int = _ // valid range: 0 - 63
50-
private[this] var levelType: Long = _ // keeps the type of each level as a bit map: 0 -> Array, 1 -> Map
51-
private[this] var levelCount: Long = _ // for each level: last bit of element count
52-
private[this] var sepRequired: Boolean = _ // whether a separator required before the next element
53-
private[this] var currentIndent: Int = _ // the number of space chars to indent with at the current level
49+
private[this] var level: Int = _ // valid range: 0 - 63
50+
private[this] var levelType: Long = _ // keeps the type of each level as a bit map: 0 -> Array, 1 -> Map
51+
private[this] var levelCount: Long = _ // for each level: last bit of element count
52+
private[this] var sepRequired: Boolean = _ // whether a separator is required before the next element
53+
private[this] var currentIndent: Int = _ // the number of space chars to indent with at the current level
54+
private[this] var indentPending: Boolean = _ // true if the next array or map element must be prefixed with an indent
5455

5556
def onNull(): Unit =
5657
if (isNotMapKey)
@@ -213,7 +214,7 @@ final private[borer] class JsonRenderer(var out: Output, indent: Int) extends Re
213214
sepRequired = false
214215
else o.failUnsupported("more than 64 JSON Array/Object nesting levels")
215216
currentIndent += indent
216-
if (currentIndent > 0) o.writeAndIndent('[') else o.writeAsByte('[')
217+
if (currentIndent > 0) o.writeAndMarkIndentPending('[') else o.writeAsByte('[')
217218
else out.failCannotBeMapKey("arrays")
218219

219220
def onMapHeader(length: Long): Unit =
@@ -229,7 +230,7 @@ final private[borer] class JsonRenderer(var out: Output, indent: Int) extends Re
229230
sepRequired = false
230231
else o.failUnsupported("more than 64 JSON Array/Object nesting levels")
231232
currentIndent += indent
232-
if (currentIndent > 0) o.writeAndIndent('{') else o.writeAsByte('{')
233+
if (currentIndent > 0) o.writeAndMarkIndentPending('{') else o.writeAsByte('{')
233234
else out.failCannotBeMapKey("maps")
234235

235236
def onBreak(): Unit =
@@ -240,7 +241,7 @@ final private[borer] class JsonRenderer(var out: Output, indent: Int) extends Re
240241
levelCount >>>= 1
241242
currentIndent -= indent
242243
else out.failValidation("Received BREAK without corresponding ArrayStart or MapStart")
243-
val o = if (indent > 0) out.writeAsByte('\n').writeIndent() else out
244+
val o = if (indent > 0 && !indentPending) out.writeAsByte('\n').writeIndent() else out
244245
out = o.writeAsByte(c).count() // level-entering items are only counted when the level is exited, not when entered
245246

246247
def onTag(value: Tag): Unit = out.failUnsupported("CBOR tags")
@@ -266,13 +267,21 @@ final private[borer] class JsonRenderer(var out: Output, indent: Int) extends Re
266267
(','.toInt + ((':'.toInt - ','.toInt) & ~((levelType.toInt & levelCount.toInt & 1) - 1))).toByte
267268
)
268269
} else writeSepIndented()
269-
} else out
270+
} else if (indent > 0 && indentPending) writePendingIndent()
271+
else out
270272

271273
private def writeSepIndented(): Output =
272-
if ((levelType & levelCount & 1) == 0) out.writeAndIndent(',') else out.writeAsBytes(':', ' ')
274+
if ((levelType & levelCount & 1) == 0) {
275+
out.writeAsBytes(',', '\n').writeIndent()
276+
} else out.writeAsBytes(':', ' ')
277+
278+
private def writeAndMarkIndentPending(c: Char): Output =
279+
indentPending = true
280+
out.writeAsByte(c)
273281

274-
private def writeAndIndent(c: Char): Output =
275-
out.writeAsBytes(c, '\n').writeIndent()
282+
private def writePendingIndent(): Output =
283+
indentPending = false
284+
out.writeAsByte('\n').writeIndent()
276285

277286
private def writeIndent(): Output =
278287
var o: Output = out

Diff for: core/src/test/scala/io/bullet/borer/PrettyJsonSpec.scala

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class PrettyJsonSpec extends ByteArrayJsonSpec:
3838
| ],
3939
| "crs": {
4040
| "type": "name",
41+
| "empty-array": [],
42+
| "empty-map": {},
4143
| "properties": {
4244
| "name": "urn:ogc:def:crs:EPSG::4326"
4345
| }

0 commit comments

Comments
 (0)