Skip to content

Commit 78ec7fe

Browse files
committed
Add docs and examples
1 parent 467bf31 commit 78ec7fe

File tree

4 files changed

+154
-38
lines changed

4 files changed

+154
-38
lines changed

README.md

Lines changed: 82 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ In order to extend pprint, subclass the PPrinter class and override the `treeify
190190
For example:
191191
```kotlin
192192
class CustomPPrinter1(val config: PPrinterConfig) : PPrinter(config) {
193-
override fun treeify(x: Any?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree =
193+
override fun treeify(x: Any?, elementName: String?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree =
194194
when (x) {
195195
is java.time.LocalDate -> Tree.Literal(x.format(DateTimeFormatter.ofPattern("MM/dd/YYYY")))
196196
else -> super.treeify(x, escapeUnicode, showFieldNames)
@@ -211,9 +211,9 @@ This printer can then be used as the basis of a custom `pprint`-like user define
211211
> You can extend it like this:
212212
> ```kotlin
213213
> class CustomPPrinter1<T>(override val serializer: SerializationStrategy<T>, override val config: PPrinterConfig) : PPrinter<T>(serializer, config) {
214-
> // Overwrite `treeifyWith` instead of treeify
215-
> override fun <R> treeifyWith(treeifyable: PPrinter.Treeifyable<R>, escapeUnicode: Boolean, showFieldNames: Boolean): Tree =
216-
> when (val v = treeifyable.value) {
214+
> // Overwrite `treeifyValueOrNull` in order to handle leaf-types. Note that anything handled here will not be treated as a composite value.
215+
> override fun <R> treeifyValueOrNull(value: R, elementName: String?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree? =
216+
> when (value) {
217217
> is LocalDate -> Tree.Literal(v.format(DateTimeFormatter.ofPattern("MM/dd/YYYY")))
218218
> else -> super.treeifyWith(treeifyable, escapeUnicode, showFieldNames)
219219
> }
@@ -240,11 +240,14 @@ class MyJavaBean(val a: String, val b: Int) {
240240
241241
// Create the custom printer
242242
class CustomPPrinter2(val config: PPrinterConfig) : PPrinter(config) {
243-
override fun treeify(x: Any?, esc: Boolean, names: Boolean): Tree =
243+
override fun treeify(x: Any?, elementName: String?, esc: Boolean, names: Boolean): Tree =
244244
when (x) {
245245
// List through the properties of 'MyJavaBean' and recursively call treeify on them.
246246
// (Note that Tree.Apply takes an iterator of properties so that the interface is lazy)
247-
is MyJavaBean -> Tree.Apply("MyJavaBean", listOf(x.getValueA(), x.getValueB()).map { treeify(it, esc, names) }.iterator())
247+
is MyJavaBean ->
248+
Tree.Apply("MyJavaBean", listOf(x.getValueA() to "A", x.getValueB() to "B")
249+
.map { (field, fieldName) -> treeify(field, fieldName, esc, names) }.iterator()
250+
)
248251
else -> super.treeify(x, esc, names)
249252
}
250253
}
@@ -258,9 +261,9 @@ println(pp.invoke(bean))
258261
To print field-names you use Tree.KeyValue:
259262
```kotlin
260263
class CustomPPrinter3(val config: PPrinterConfig) : PPrinter(config) {
261-
override fun treeify(x: Any?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree {
264+
override fun treeify(x: Any?, elementName: String?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree {
262265
// function to make recursive calls shorter
263-
fun rec(x: Any?) = treeify(x, escapeUnicode, showFieldNames)
266+
fun rec(x: Any?) = treeify(x, null, escapeUnicode, showFieldNames)
264267
return when (x) {
265268
// Recurse on the values, pass result into Tree.KeyValue.
266269
is MyJavaBean ->
@@ -352,6 +355,77 @@ When using sequences, you will need to annotate the
352355
sequence-field using `@Serializable(with = PPrintSequenceSerializer::class)`.
353356
See the note in the [Infinite Sequences in Kotlin Multiplatform](#infinite-sequences-in-kotlin-multiplatform) section for more detail.
354357
358+
## Using `elementName` metadata
359+
360+
Note that the `elementName` parameter will contain the name of the field of the data class that is being printed.
361+
You can use this to exclude particular fields. For example:
362+
```kotlin
363+
class CustomPPrinter6(config: PPrinterConfig) : PPrinter(config) {
364+
override fun treeify(x: Any?, elementName: String?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree =
365+
when {
366+
elementName == "born" -> Tree.Literal("REDACTED", elementName)
367+
else -> super.treeify(x, elementName, escapeUnicode, showFieldNames)
368+
}
369+
}
370+
371+
data class Person(val name: String, val born: LocalDate)
372+
val pp = CustomPPrinter6(PPrinterConfig())
373+
val joe = Person("Joe", LocalDate.of(1981, 1, 1))
374+
375+
println(pp.invoke(joe))
376+
//> Person(name = "Joe", born = REDACTED)
377+
```
378+
379+
You can also filter out fields on the parent-element level like this:
380+
```kotlin
381+
data class PersonBorn(val name: String, val born: LocalDate)
382+
383+
class CustomPPrinter5(config: PPrinterConfig) : PPrinter(config) {
384+
override fun treeify(x: Any?, elementName: String?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree =
385+
when {
386+
x is PersonBorn ->
387+
when (val p = super.treeify(x, elementName, escapeUnicode, showFieldNames)) {
388+
is Tree.Apply -> p.copy(body = p.body.asSequence().toList().filter { it.elementName != "born" }.iterator())
389+
else -> error("Expected Tree.Apply")
390+
}
391+
else ->
392+
super.treeify(x, elementName, escapeUnicode, showFieldNames)
393+
}
394+
}
395+
396+
val p = PersonBorn("Joe", LocalDate.of(1981, 1, 1))
397+
println(CustomPPrinter5(PPrinterConfig()).invoke(p))
398+
//> PersonBorn(name = "Joe")
399+
```
400+
401+
## Using `elementName` metadata - Kotlin Multiplatform
402+
403+
If you want to filter out fields based on `elementName` in Kotlin Multiplatform inside of the PPrinter you need to override
404+
the `treeifyComposite` method.
405+
> Since `treeifyValueOrNull` will always be attempted, trying to do super.treeify (or super.treeifyComposite) inside of it will result in a stack-overflow.
406+
407+
For example:
408+
```kotlin
409+
@Serializable
410+
data class PersonBorn(val name: String, val born: Long)
411+
412+
class CustomPPrinter6<T>(override val serializer: SerializationStrategy<T>, override val config: PPrinterConfig) : PPrinter<T>(serializer, config) {
413+
override fun <E> treeifyComposite(elem: Treeifyable.Elem<E>, elementName: String?, showFieldNames: Boolean): Tree =
414+
when(elem.value) {
415+
is PersonBorn ->
416+
when (val p = super.treeifyComposite(elem, elementName, showFieldNames)) {
417+
is Tree.Apply -> p.copy(body = p.body.asSequence().toList().filter { it.elementName != "born" }.iterator())
418+
else -> error("Expected Tree.Apply")
419+
}
420+
else -> super.treeifyComposite(elem, elementName, showFieldNames)
421+
}
422+
}
423+
424+
val p = PersonBorn("Joe", 1234567890)
425+
println(CustomPPrinter6<PersonBorn>(PersonBorn.serializer(), PPrinterConfig()).invoke(p))
426+
//> PersonBorn(name = "Joe")
427+
```
428+
355429
#### Sealed Hierarchies in KMP
356430
357431
According to the `kotlinx-serialization` documentation, every member of a sealed hierarchy must be annotated with `@Serializable`.
@@ -388,20 +462,6 @@ class PPrintSequenceSerializer<T>(val element: KSerializer<T>) : KSerializer<Seq
388462
```
389463
(Note that a real user-defined serialzier for `Sequence` will work as well.)
390464
391-
The actual handling of sequence printing is done in the `treeifyWith` method (roughly) like this:
392-
```kotlin
393-
open fun <R> treeifyWith(treeifyable: Treeifyable<R>, escapeUnicode: Boolean, showFieldNames: Boolean): Tree =
394-
when {
395-
treeifyable is Sequence<*> && treeifyable is Treeifyable.Elem && treeifyable.serializer is PPrintSequenceSerializer<*> -> {
396-
@Suppress("UNCHECKED_CAST")
397-
val elementSerializer = treeifyable.serializer.element as KSerializer<Any?>
398-
Tree.Apply("Sequence", value.map { treeifyWith(Treeifyable.Elem(it, elementSerializer), escapeUnicode, showFieldNames) }.iterator())
399-
}
400-
else -> super.treeifyWith(treeifyable, escapeUnicode, showFieldNames)
401-
}
402-
```
403-
You can follow this pattern to define PPrintable serializers for other generic types.
404-
405465
#### General Note on Generic ADTs and KMP
406466
407467
Due to issues in kotlinx-serialization like [#1341](https://github.com/Kotlin/kotlinx.serialization/issues/1341) there are cases

pprint-kotlin-kmp/src/jvmTest/kotlin/io/exoquery/pprint/KmpExamples.kt

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,6 @@ data class Stuff(val value: Map<String, String>)
1919
@Serializable
2020
data class IterStuff(val value: Iterator<Int>)
2121

22-
@OptIn(ExperimentalSerializationApi::class)
23-
fun main() {
24-
// showMap()
25-
// showPerson()
26-
// showIteratorInObject()
27-
// mutualRecurse()
28-
// usingSequence0()
29-
// usingSequence1()
30-
// customPrinter()
31-
32-
// GADT1.gadt()
33-
GADT2.gadt()
34-
}
35-
3622
// Problematic Cases with GADTs 1
3723
object GADT1 {
3824
@Serializable
@@ -159,3 +145,40 @@ fun showPerson() {
159145
val str = printer(per)
160146
println(str)
161147
}
148+
149+
@Serializable
150+
data class PersonBorn(val name: String, val born: Long)
151+
152+
class CustomPPrinter6<T>(override val serializer: SerializationStrategy<T>, override val config: PPrinterConfig) : PPrinter<T>(serializer, config) {
153+
override fun <E> treeifyComposite(elem: Treeifyable.Elem<E>, elementName: String?, showFieldNames: Boolean): Tree =
154+
when(elem.value) {
155+
is PersonBorn ->
156+
when (val p = super.treeifyComposite(elem, elementName, showFieldNames)) {
157+
is Tree.Apply -> p.copy(body = p.body.asSequence().toList().filter { it.elementName != "born" }.iterator())
158+
else -> error("Expected Tree.Apply")
159+
}
160+
else -> super.treeifyComposite(elem, elementName, showFieldNames)
161+
}
162+
}
163+
164+
fun customPrinter6() {
165+
val joe = PersonBorn("Joe", 123)
166+
val printer = CustomPPrinter6<PersonBorn>(PersonBorn.serializer(), PPrinterConfig())
167+
val p = printer(joe)
168+
println(p)
169+
}
170+
171+
@OptIn(ExperimentalSerializationApi::class)
172+
fun main() {
173+
// showMap()
174+
// showPerson()
175+
// showIteratorInObject()
176+
// mutualRecurse()
177+
// usingSequence0()
178+
// usingSequence1()
179+
// customPrinter()
180+
181+
// GADT1.gadt()
182+
//GADT2.gadt()
183+
customPrinter6()
184+
}

pprint-kotlin/src/main/kotlin/io/exoquery/pprint/ProductSupport.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ object ProductSupport {
3333
): Iterator<Tree>
3434
{
3535
val props = cls.dataClassProperties()
36-
val productIterator = props.asSequence().map { it.invoke(x) }
3736
val productElementNames = props.asSequence().map { it.name }
3837

3938
return productElementNames

pprint-kotlin/src/test/kotlin/io/exoquery/pprint/Examples.kt

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,42 @@ fun ex10() = run {
140140
println(CustomPPrinter4(PPrinterConfig(defaultShowFieldNames = false)).invoke(bean))
141141
}
142142

143+
data class PersonBorn(val name: String, val born: LocalDate)
144+
145+
class CustomPPrinter5(config: PPrinterConfig) : PPrinter(config) {
146+
override fun treeify(x: Any?, elementName: String?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree =
147+
when {
148+
x is PersonBorn ->
149+
when (val p = super.treeify(x, elementName, escapeUnicode, showFieldNames)) {
150+
is Tree.Apply -> p.copy(body = p.body.asSequence().toList().filter { it.elementName != "born" }.iterator())
151+
else -> error("Expected Tree.Apply")
152+
}
153+
else ->
154+
super.treeify(x, elementName, escapeUnicode, showFieldNames)
155+
}
156+
}
157+
158+
fun ex11() = run {
159+
val p = PersonBorn("Joe", LocalDate.of(1981, 1, 1))
160+
println(CustomPPrinter5(PPrinterConfig()).invoke(p))
161+
}
162+
163+
class CustomPPrinter6(config: PPrinterConfig) : PPrinter(config) {
164+
override fun treeify(x: Any?, elementName: String?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree =
165+
when {
166+
elementName == "born" -> Tree.Literal("REDACTED", elementName)
167+
else -> super.treeify(x, elementName, escapeUnicode, showFieldNames)
168+
}
169+
}
170+
171+
fun ex12() = run {
172+
val p = PersonBorn("Joe", LocalDate.of(1981, 1, 1))
173+
println(CustomPPrinter6(PPrinterConfig()).invoke(p))
174+
}
175+
176+
143177
fun main() {
144-
ex10()
178+
ex12()
145179

146180

147181
//val seq = generateSequence { "foo" }

0 commit comments

Comments
 (0)