kotlinx.serialization.csv.encode.CsvEncoder.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlinx-serialization-csv Show documentation
Show all versions of kotlinx-serialization-csv Show documentation
Library to easily use Kotlin Serialization to serialize to/from CSV.
package kotlinx.serialization.csv.encode
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.csv.Csv
import kotlinx.serialization.descriptors.PolymorphicKind
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.SerialKind
import kotlinx.serialization.descriptors.StructureKind
import kotlinx.serialization.encoding.AbstractEncoder
import kotlinx.serialization.encoding.CompositeEncoder
import kotlinx.serialization.modules.SerializersModule
/**
* Default CSV encoder.
*/
@OptIn(ExperimentalSerializationApi::class)
internal abstract class CsvEncoder(
protected val csv: Csv,
protected val writer: CsvWriter,
private val parent: CsvEncoder?
) : AbstractEncoder() {
override val serializersModule: SerializersModule = csv.serializersModule
protected val configuration
get() = csv.configuration
override fun beginCollection(
descriptor: SerialDescriptor,
collectionSize: Int,
): CompositeEncoder {
encodeCollectionSize(collectionSize)
return super.beginCollection(descriptor, collectionSize)
}
override fun beginStructure(
descriptor: SerialDescriptor,
vararg typeSerializers: KSerializer<*>
): CompositeEncoder {
return when (descriptor.kind) {
StructureKind.LIST,
StructureKind.MAP ->
SimpleCsvEncoder(csv, writer, this)
StructureKind.CLASS ->
SimpleCsvEncoder(csv, writer, this)
StructureKind.OBJECT ->
ObjectCsvEncoder(csv, writer, this)
PolymorphicKind.SEALED ->
SealedCsvEncoder(csv, writer, this, descriptor)
PolymorphicKind.OPEN ->
SimpleCsvEncoder(csv, writer, this)
else ->
error("CSV does not support '${descriptor.kind}'.")
}
}
override fun endStructure(descriptor: SerialDescriptor) {
parent?.endChildStructure(descriptor)
}
protected open fun endChildStructure(desc: SerialDescriptor) {
}
override fun encodeByte(value: Byte) {
encodeColumn(value.toString(), isNumeric = true)
}
override fun encodeShort(value: Short) {
encodeColumn(value.toString(), isNumeric = true)
}
override fun encodeInt(value: Int) {
encodeColumn(value.toString(), isNumeric = true)
}
override fun encodeLong(value: Long) {
encodeColumn(value.toString(), isNumeric = true)
}
override fun encodeFloat(value: Float) {
encodeColumn(value.toString(), isNumeric = true)
}
override fun encodeDouble(value: Double) {
encodeColumn(value.toString(), isNumeric = true)
}
override fun encodeBoolean(value: Boolean) {
encodeColumn(value.toString(), isNumeric = false)
}
override fun encodeChar(value: Char) {
encodeColumn(value.toString())
}
override fun encodeString(value: String) {
encodeColumn(value)
}
override fun encodeNull() {
encodeColumn(configuration.nullString, isNull = true)
}
override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) {
encodeColumn(enumDescriptor.getElementName(index))
}
protected fun printHeaderRecord(desc: SerialDescriptor) {
writer.beginRecord()
printHeader("", desc)
writer.endRecord()
}
private fun printHeader(prefix: String, desc: SerialDescriptor) {
when (desc.kind) {
is StructureKind.LIST,
is StructureKind.MAP,
is PolymorphicKind.OPEN -> {
error("CSV headers are not supported for variable sized type '${desc.kind}'.")
}
}
for (i in 0 until desc.elementsCount) {
val name = prefix + desc.getElementName(i)
val childDesc = desc.getElementDescriptor(i)
when {
// TODO Check
childDesc.kind is SerialKind.CONTEXTUAL ->
writer.printColumn(name)
// TODO Check
childDesc.kind is SerialKind.ENUM ->
writer.printColumn(name)
childDesc.kind is PolymorphicKind.SEALED -> {
writer.printColumn(name)
val headerSeparator = configuration.headerSeparator
printHeader("$name$headerSeparator", childDesc.getElementDescriptor(1))
}
childDesc.kind is StructureKind.OBJECT ->
Unit
childDesc.elementsCount > 0 -> {
val headerSeparator = configuration.headerSeparator
printHeader("$name$headerSeparator", childDesc)
}
else ->
writer.printColumn(name)
}
}
}
protected open fun encodeCollectionSize(collectionSize: Int) {
writer.printColumn(collectionSize.toString(), isNumeric = true)
}
protected open fun encodeColumn(
value: String,
isNumeric: Boolean = false,
isNull: Boolean = false
) {
writer.printColumn(value, isNumeric, isNull)
}
}