Update default polymorphic serialization guide section (#1031)

* Update polymorphic guide

    * Explain limitations of proposed default serializer
    * Add JSON section for maintaining an arbitrary attributes

Fixes #1025

Co-authored-by: Roman Elizarov <elizarov@gmail.com>
diff --git a/core/commonMain/src/kotlinx/serialization/modules/PolymorphicModuleBuilder.kt b/core/commonMain/src/kotlinx/serialization/modules/PolymorphicModuleBuilder.kt
index 0ef5327..57720c3 100644
--- a/core/commonMain/src/kotlinx/serialization/modules/PolymorphicModuleBuilder.kt
+++ b/core/commonMain/src/kotlinx/serialization/modules/PolymorphicModuleBuilder.kt
@@ -41,6 +41,8 @@
      * type and have a precise serializer, so the default serializer has limited capabilities.
      * To have a structural access to the unknown data, it is recommended to use [JsonTransformingSerializer]
      * or [JsonContentPolymorphicSerializer] classes.
+     *
+     * Default serializers provider affects only deserialization process.
      */
     public fun default(defaultSerializerProvider: (className: String?) -> DeserializationStrategy<out Base>?) {
         require(this.defaultSerializerProvider == null) {
diff --git a/docs/json.md b/docs/json.md
index 5e74537..61d6aca 100644
--- a/docs/json.md
+++ b/docs/json.md
@@ -29,6 +29,7 @@
   * [Manipulating default values](#manipulating-default-values)
   * [Content-based polymorphic deserialization](#content-based-polymorphic-deserialization)
   * [Under the hood (experimental)](#under-the-hood-experimental)
+  * [Maintaining custom JSON attributes](#maintaining-custom-json-attributes)
 
 <!--- END -->
 
@@ -812,6 +813,68 @@
 
 <!--- TEST -->
 
+### Maintaining custom JSON attributes  
+  
+A good example of custom JSON-specific serializer would be a deserializer 
+that packs all unknown JSON properties into a dedicated field of `JsonObject` type.  
+
+Let us add `UnknownProject` &ndash; class with basic `name` property and arbitrary details flattened into the same object:
+
+<!--- INCLUDE
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+-->
+
+```kotlin
+data class UnknownProject(val name: String, val details: JsonObject)
+```
+
+However, the default plugin-generated serializer requires details 
+to be a separate JSON object and that's not what we want.
+
+To mitigate that, we can write our own serializer that leverages the fact that it can only be used with `Json` format
+
+```kotlin
+object UnknownProjectSerializer : KSerializer<UnknownProject> {
+    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("UnknownProject") {
+        element<String>("name")
+        element<JsonElement>("details")
+    }
+
+    override fun deserialize(decoder: Decoder): UnknownProject {
+        // Cast to JSON-specific interface
+        val jsonInput = decoder as? JsonDecoder ?: error("Can be deserialized only by JSON")
+        // Read the whole content as JSON
+        val json = jsonInput.decodeJsonElement().jsonObject
+        // Extract and remove name property
+        val name = json.getValue("name").jsonPrimitive.content
+        val details = json.toMutableMap()
+        details.remove("name")
+        return UnknownProject(name, JsonObject(details))
+    }
+
+    override fun serialize(encoder: Encoder, value: UnknownProject) {
+        error("Serialization is not supported")
+    }
+}
+```
+  
+Now it can be used to read flattened JSON details as `UnknownProject`.
+
+```kotlin
+fun main() {
+    println(Json.decodeFromString(UnknownProjectSerializer, """{"type":"unknown","name":"example","maintainer":"Unknown","license":"Apache 2.0"}"""))
+}
+```  
+
+> You can get the full code [here](../guide/example/example-json-18.kt).
+
+```text
+UnknownProject(name=example, details={"type":"unknown","maintainer":"Unknown","license":"Apache 2.0"})
+```
+
+<!--- TEST -->
+
 ---
 
 The next chapter covers [Alternative and custom formats (experimental)](formats.md).
diff --git a/docs/polymorphism.md b/docs/polymorphism.md
index 7e611cb..01ea1f6 100644
--- a/docs/polymorphism.md
+++ b/docs/polymorphism.md
@@ -855,6 +855,11 @@
 
 <!--- TEST -->
 
+We used a plugin-generated serializer as a default serializer, implying that 
+the structure of the "unknown" data is known in advance. In a real-world API it's rarely the case.
+For that purpose a custom, less-structured serializer is needed. You will see the example of such serializer in the future section
+on [Maintaining custom JSON attributes](json.md#maintaining-custom-json-attributes).
+
 ---
 
 The next chapter covers [JSON features](json.md).
diff --git a/docs/serialization-guide.md b/docs/serialization-guide.md
index 1c13638..f32658e 100644
--- a/docs/serialization-guide.md
+++ b/docs/serialization-guide.md
@@ -122,6 +122,7 @@
   * <a name='manipulating-default-values'></a>[Manipulating default values](json.md#manipulating-default-values)
   * <a name='content-based-polymorphic-deserialization'></a>[Content-based polymorphic deserialization](json.md#content-based-polymorphic-deserialization)
   * <a name='under-the-hood-experimental'></a>[Under the hood (experimental)](json.md#under-the-hood-experimental)
+  * <a name='maintaining-custom-json-attributes'></a>[Maintaining custom JSON attributes](json.md#maintaining-custom-json-attributes)
 <!--- END -->
 
 **Chapter 6.** [Alternative and custom formats (experimental)](formats.md)
diff --git a/guide/example/example-json-18.kt b/guide/example/example-json-18.kt
new file mode 100644
index 0000000..325472f
--- /dev/null
+++ b/guide/example/example-json-18.kt
@@ -0,0 +1,37 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson18
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+
+data class UnknownProject(val name: String, val details: JsonObject)
+
+object UnknownProjectSerializer : KSerializer<UnknownProject> {
+    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("UnknownProject") {
+        element<String>("name")
+        element<JsonElement>("details")
+    }
+
+    override fun deserialize(decoder: Decoder): UnknownProject {
+        // Cast to JSON-specific interface
+        val jsonInput = decoder as? JsonDecoder ?: error("Can be deserialized only by JSON")
+        // Read the whole content as JSON
+        val json = jsonInput.decodeJsonElement().jsonObject
+        // Extract and remove name property
+        val name = json.getValue("name").jsonPrimitive.content
+        val details = json.toMutableMap()
+        details.remove("name")
+        return UnknownProject(name, JsonObject(details))
+    }
+
+    override fun serialize(encoder: Encoder, value: UnknownProject) {
+        error("Serialization is not supported")
+    }
+}
+
+fun main() {
+    println(Json.decodeFromString(UnknownProjectSerializer, """{"type":"unknown","name":"example","maintainer":"Unknown","license":"Apache 2.0"}"""))
+}
diff --git a/guide/test/JsonTest.kt b/guide/test/JsonTest.kt
index 896fe74..0fe3a88 100644
--- a/guide/test/JsonTest.kt
+++ b/guide/test/JsonTest.kt
@@ -130,4 +130,11 @@
             "[Ok(data=Project(name=kotlinx.serialization)), Error(message=Not found)]"
         )
     }
+
+    @Test
+    fun testExampleJson18() {
+        captureOutput("ExampleJson18") { example.exampleJson18.main() }.verifyOutputLines(
+            "UnknownProject(name=example, details={\"type\":\"unknown\",\"maintainer\":\"Unknown\",\"license\":\"Apache 2.0\"})"
+        )
+    }
 }