forked from kscience/visionforge
Updated serialization utils
This commit is contained in:
parent
b72300bfaa
commit
58e396f50c
@ -0,0 +1,169 @@
|
||||
package hep.dataforge.vis.spatial.gdml
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.vis.spatial.*
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlinx.serialization.modules.SerialModule
|
||||
import kotlinx.serialization.modules.SerialModuleCollector
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal val SerialDescriptor.jsonType
|
||||
get() = when (this.kind) {
|
||||
StructureKind.LIST -> "array"
|
||||
PrimitiveKind.BYTE, PrimitiveKind.SHORT, PrimitiveKind.INT, PrimitiveKind.LONG,
|
||||
PrimitiveKind.FLOAT, PrimitiveKind.DOUBLE -> "number"
|
||||
PrimitiveKind.STRING, PrimitiveKind.CHAR, UnionKind.ENUM_KIND -> "string"
|
||||
PrimitiveKind.BOOLEAN -> "boolean"
|
||||
else -> "object"
|
||||
}
|
||||
|
||||
|
||||
private fun SerialModule.enumerate(type: KClass<*>): Sequence<SerialDescriptor> {
|
||||
val list = ArrayList<SerialDescriptor>()
|
||||
fun send(descriptor: SerialDescriptor) = list.add(descriptor)
|
||||
|
||||
val enumerator = object : SerialModuleCollector {
|
||||
override fun <T : Any> contextual(kClass: KClass<T>, serializer: KSerializer<T>) {
|
||||
if (kClass == type) {
|
||||
send(serializer.descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun <Base : Any, Sub : Base> polymorphic(
|
||||
baseClass: KClass<Base>,
|
||||
actualClass: KClass<Sub>,
|
||||
actualSerializer: KSerializer<Sub>
|
||||
) {
|
||||
if (baseClass == type) {
|
||||
send(actualSerializer.descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
dumpTo(enumerator)
|
||||
return list.asSequence()
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an [JsonObject] which contains Json Schema of given [descriptor].
|
||||
*
|
||||
* Schema can contain following fields:
|
||||
* `description`, `type` for all descriptors;
|
||||
* `properties` and `required` for objects;
|
||||
* `enum` for enums;
|
||||
* `items` for arrays.
|
||||
*
|
||||
* User can modify this schema to add additional validation keywords
|
||||
* (as per [https://json-schema.org/latest/json-schema-validation.html])
|
||||
* if they want.
|
||||
*/
|
||||
private fun jsonSchema(descriptor: SerialDescriptor, context: SerialModule): JsonObject {
|
||||
|
||||
if (descriptor.name in arrayOf(
|
||||
"hep.dataforge.vis.spatial.Point3D",
|
||||
"hep.dataforge.vis.spatial.Point2D",
|
||||
Meta::class.qualifiedName
|
||||
)
|
||||
) return json {
|
||||
"\$ref" to "#/definitions/${descriptor.name}"
|
||||
}
|
||||
|
||||
|
||||
val properties: MutableMap<String, JsonObject> = mutableMapOf()
|
||||
val requiredProperties: MutableSet<String> = mutableSetOf()
|
||||
val isEnum = descriptor.kind == UnionKind.ENUM_KIND
|
||||
val isPolymorphic = descriptor.kind == UnionKind.POLYMORPHIC
|
||||
|
||||
|
||||
if (!isEnum && !isPolymorphic) descriptor.elementDescriptors().forEachIndexed { index, child ->
|
||||
val elementName = descriptor.getElementName(index)
|
||||
|
||||
properties[elementName] = when (elementName) {
|
||||
"templates" -> json {
|
||||
"\$ref" to "#/definitions/hep.dataforge.vis.spatial.VisualGroup3D"
|
||||
}
|
||||
"properties" -> json {
|
||||
"\$ref" to "#/definitions/${Meta::class.qualifiedName}"
|
||||
}
|
||||
"first", "second" -> json{
|
||||
"\$ref" to "#/definitions/children"
|
||||
}
|
||||
"styleSheet" -> json {
|
||||
"type" to "object"
|
||||
"additionalProperties" to json {
|
||||
"\$ref" to "#/definitions/${Meta::class.qualifiedName}"
|
||||
}
|
||||
}
|
||||
in arrayOf("children") -> json {
|
||||
"type" to "object"
|
||||
"additionalProperties" to json {
|
||||
"\$ref" to "#/definitions/children"
|
||||
}
|
||||
}
|
||||
else -> jsonSchema(child, context)
|
||||
}
|
||||
|
||||
if (!descriptor.isElementOptional(index)) requiredProperties.add(elementName)
|
||||
}
|
||||
|
||||
val jsonType = descriptor.jsonType
|
||||
val objectData: MutableMap<String, JsonElement> = mutableMapOf(
|
||||
"description" to JsonLiteral(descriptor.name),
|
||||
"type" to JsonLiteral(jsonType)
|
||||
)
|
||||
if (isEnum) {
|
||||
val allElementNames = (0 until descriptor.elementsCount).map(descriptor::getElementName)
|
||||
objectData += "enum" to JsonArray(allElementNames.map(::JsonLiteral))
|
||||
}
|
||||
when (jsonType) {
|
||||
"object" -> {
|
||||
objectData["properties"] = JsonObject(properties)
|
||||
val required = requiredProperties.map { JsonLiteral(it) }
|
||||
if (required.isNotEmpty()) {
|
||||
objectData["required"] = JsonArray(required)
|
||||
}
|
||||
}
|
||||
"array" -> objectData["items"] = properties.values.let {
|
||||
check(it.size == 1) { "Array descriptor has returned inconsistent number of elements: expected 1, found ${it.size}" }
|
||||
it.first()
|
||||
}
|
||||
else -> { /* no-op */
|
||||
}
|
||||
}
|
||||
return JsonObject(objectData)
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val context = Visual3DPlugin.serialModule
|
||||
val definitions = json {
|
||||
"children" to json {
|
||||
"anyOf" to jsonArray {
|
||||
context.enumerate(VisualObject3D::class).forEach {
|
||||
if (it.name == "hep.dataforge.vis.spatial.VisualGroup3D") {
|
||||
+json {
|
||||
"\$ref" to "#/definitions/${it.name}"
|
||||
}
|
||||
} else {
|
||||
+jsonSchema(it, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"hep.dataforge.vis.spatial.Point3D" to jsonSchema(Point3DSerializer.descriptor, context)
|
||||
"hep.dataforge.vis.spatial.Point2D" to jsonSchema(Point2DSerializer.descriptor, context)
|
||||
"hep.dataforge.vis.spatial.VisualGroup3D" to jsonSchema(VisualGroup3D.serializer().descriptor, context)
|
||||
|
||||
}
|
||||
|
||||
println(
|
||||
Json.indented.stringify(
|
||||
JsonObjectSerializer,
|
||||
json {
|
||||
"definitions" to definitions
|
||||
"\$ref" to "#/definitions/hep.dataforge.vis.spatial.VisualGroup3D"
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.io.ConfigSerializer
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.vis.common.AbstractVisualObject
|
||||
import hep.dataforge.vis.common.VisualFactory
|
||||
|
@ -1,7 +1,7 @@
|
||||
@file:UseSerializers(Point3DSerializer::class)
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.io.ConfigSerializer
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.meta.MetaBuilder
|
||||
import hep.dataforge.meta.update
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.io.ConfigSerializer
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.vis.common.AbstractVisualObject
|
||||
import kotlinx.serialization.Serializable
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.io.ConfigSerializer
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.meta.MetaBuilder
|
||||
import hep.dataforge.vis.common.AbstractVisualObject
|
||||
|
@ -1,7 +1,7 @@
|
||||
@file:UseSerializers(Point2DSerializer::class, Point3DSerializer::class)
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.io.ConfigSerializer
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.vis.common.AbstractVisualObject
|
||||
import kotlinx.serialization.Serializable
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.io.ConfigSerializer
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.vis.common.AbstractVisualObject
|
||||
import hep.dataforge.vis.common.number
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.io.ConfigSerializer
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
|
||||
import hep.dataforge.io.serialization.NameSerializer
|
||||
import hep.dataforge.meta.*
|
||||
@ -15,6 +15,7 @@ import hep.dataforge.vis.common.MutableVisualGroup
|
||||
import hep.dataforge.vis.common.VisualGroup
|
||||
import hep.dataforge.vis.common.VisualObject
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import kotlin.collections.component1
|
||||
import kotlin.collections.component2
|
||||
@ -72,6 +73,7 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
|
||||
ProxyChild(it.key.asName())
|
||||
} ?: emptyMap()
|
||||
|
||||
@Transient
|
||||
private val propertyCache: HashMap<Name, Config> = HashMap()
|
||||
|
||||
fun childPropertyName(childName: Name, propertyName: Name): Name {
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.io.ConfigSerializer
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.vis.common.AbstractVisualObject
|
||||
import kotlinx.serialization.Serializable
|
||||
|
@ -1,7 +1,7 @@
|
||||
@file:UseSerializers(Point3DSerializer::class)
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.io.ConfigSerializer
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.vis.common.AbstractVisualObject
|
||||
import kotlinx.serialization.Serializable
|
||||
|
@ -4,8 +4,8 @@ import hep.dataforge.context.AbstractPlugin
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.context.PluginFactory
|
||||
import hep.dataforge.context.PluginTag
|
||||
import hep.dataforge.io.ConfigSerializer
|
||||
import hep.dataforge.io.MetaSerializer
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
import hep.dataforge.io.serialization.MetaSerializer
|
||||
import hep.dataforge.io.serialization.NameSerializer
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.Name
|
||||
|
@ -8,8 +8,8 @@
|
||||
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.io.ConfigSerializer
|
||||
import hep.dataforge.io.MetaSerializer
|
||||
import hep.dataforge.io.serialization.ConfigSerializer
|
||||
import hep.dataforge.io.serialization.MetaSerializer
|
||||
import hep.dataforge.io.serialization.NameSerializer
|
||||
import hep.dataforge.meta.Config
|
||||
import hep.dataforge.meta.Meta
|
||||
|
@ -1,45 +1,97 @@
|
||||
package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.io.serialization.descriptor
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.toName
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.internal.DoubleSerializer
|
||||
import kotlinx.serialization.internal.StringDescriptor
|
||||
import kotlinx.serialization.internal.nullable
|
||||
|
||||
@Serializable
|
||||
private data class Point2DSerial(val x: Double, val y: Double)
|
||||
inline fun <R> Decoder.decodeStructure(
|
||||
desc: SerialDescriptor,
|
||||
vararg typeParams: KSerializer<*> = emptyArray(),
|
||||
crossinline block: CompositeDecoder.() -> R
|
||||
): R {
|
||||
val decoder = beginStructure(desc, *typeParams)
|
||||
val res = decoder.block()
|
||||
decoder.endStructure(desc)
|
||||
return res
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class Point3DSerial(val x: Double, val y: Double, val z: Double)
|
||||
inline fun Encoder.encodeStructure(
|
||||
desc: SerialDescriptor,
|
||||
vararg typeParams: KSerializer<*> = emptyArray(),
|
||||
block: CompositeEncoder.() -> Unit
|
||||
) {
|
||||
val encoder = beginStructure(desc, *typeParams)
|
||||
encoder.block()
|
||||
encoder.endStructure(desc)
|
||||
}
|
||||
|
||||
@Serializer(Point3D::class)
|
||||
object Point3DSerializer : KSerializer<Point3D> {
|
||||
private val serializer = Point3DSerial.serializer()
|
||||
override val descriptor: SerialDescriptor get() = serializer.descriptor
|
||||
override val descriptor: SerialDescriptor = descriptor("hep.dataforge.vis.spatial.Point3D") {
|
||||
double("x", true)
|
||||
double("y", true)
|
||||
double("z", true)
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): Point3D {
|
||||
return serializer.deserialize(decoder).let {
|
||||
Point3D(it.x, it.y, it.z)
|
||||
var x: Double? = null
|
||||
var y: Double? = null
|
||||
var z: Double? = null
|
||||
decoder.decodeStructure(descriptor) {
|
||||
loop@ while (true) {
|
||||
when (val i = decodeElementIndex(descriptor)) {
|
||||
CompositeDecoder.READ_DONE -> break@loop
|
||||
0 -> x = decodeNullableSerializableElement(descriptor, 0, DoubleSerializer.nullable) ?: 0.0
|
||||
1 -> y = decodeNullableSerializableElement(descriptor, 0, DoubleSerializer.nullable) ?: 0.0
|
||||
2 -> z = decodeNullableSerializableElement(descriptor, 0, DoubleSerializer.nullable) ?: 0.0
|
||||
else -> throw SerializationException("Unknown index $i")
|
||||
}
|
||||
}
|
||||
}
|
||||
return Point3D(x?:0.0, y?:0.0, z?:0.0)
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, obj: Point3D) {
|
||||
serializer.serialize(encoder, Point3DSerial(obj.x, obj.y, obj.z))
|
||||
encoder.encodeStructure(descriptor) {
|
||||
if (obj.x != 0.0) encodeDoubleElement(descriptor, 0, obj.x)
|
||||
if (obj.y != 0.0) encodeDoubleElement(descriptor, 1, obj.y)
|
||||
if (obj.z != 0.0) encodeDoubleElement(descriptor, 2, obj.z)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializer(Point2D::class)
|
||||
object Point2DSerializer : KSerializer<Point2D> {
|
||||
private val serializer = Point2DSerial.serializer()
|
||||
override val descriptor: SerialDescriptor get() = serializer.descriptor
|
||||
override val descriptor: SerialDescriptor = descriptor("hep.dataforge.vis.spatial.Point2D") {
|
||||
double("x", true)
|
||||
double("y", true)
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): Point2D {
|
||||
return serializer.deserialize(decoder).let {
|
||||
Point2D(it.x, it.y)
|
||||
var x: Double? = null
|
||||
var y: Double? = null
|
||||
decoder.decodeStructure(descriptor) {
|
||||
loop@ while (true) {
|
||||
when (val i = decodeElementIndex(descriptor)) {
|
||||
CompositeDecoder.READ_DONE -> break@loop
|
||||
0 -> x = decodeNullableSerializableElement(descriptor, 0, DoubleSerializer.nullable) ?: 0.0
|
||||
1 -> y = decodeNullableSerializableElement(descriptor, 0, DoubleSerializer.nullable) ?: 0.0
|
||||
else -> throw SerializationException("Unknown index $i")
|
||||
}
|
||||
}
|
||||
}
|
||||
return Point2D(x?:0.0, y?:0.0)
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, obj: Point2D) {
|
||||
serializer.serialize(encoder, Point2DSerial(obj.x, obj.y))
|
||||
encoder.encodeStructure(descriptor) {
|
||||
if (obj.x != 0.0) encodeDoubleElement(descriptor, 0, obj.x)
|
||||
if (obj.y != 0.0) encodeDoubleElement(descriptor, 1, obj.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,8 @@ class SerializationTest {
|
||||
fun testCubeSerialization(){
|
||||
val cube = Box(100f,100f,100f).apply{
|
||||
color(222)
|
||||
x = 100
|
||||
z = -100
|
||||
}
|
||||
val string = json.stringify(Box.serializer(),cube)
|
||||
println(string)
|
||||
|
Loading…
Reference in New Issue
Block a user