custom JSON serialization (no universal serialization yet)
This commit is contained in:
parent
1926d157ee
commit
a5a4b67c97
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,7 +3,7 @@
|
|||||||
*.iws
|
*.iws
|
||||||
out/
|
out/
|
||||||
.gradle
|
.gradle
|
||||||
/build/
|
**/build/
|
||||||
|
|
||||||
|
|
||||||
!gradle-wrapper.jar
|
!gradle-wrapper.jar
|
||||||
|
24
build.gradle
24
build.gradle
@ -1,26 +1,40 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.2.70'
|
ext.kotlin_version = '1.3.0-rc-57'
|
||||||
ext.serialization_version = '0.6.2'
|
ext.serialization_version = '0.8.0-rc13'
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
maven { url "https://kotlin.bintray.com/kotlinx" }
|
maven {
|
||||||
|
url = "http://dl.bintray.com/kotlin/kotlin-eap"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath "org.jetbrains.kotlinx:kotlinx-gradle-serialization-plugin:$serialization_version"
|
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//plugins {
|
||||||
|
// id 'kotlin-platform-common' version "$kotlin_version" apply false
|
||||||
|
// id 'kotlin-platform-jvm' version "$kotlin_version" apply false
|
||||||
|
// id 'kotlin-platform-js' version "$kotlin_version" apply false
|
||||||
|
// id 'kotlinx-serialization' version "$kotlin_version" apply false
|
||||||
|
//}
|
||||||
|
|
||||||
description = "The basic interfaces for DataForge meta-data"
|
description = "The basic interfaces for DataForge meta-data"
|
||||||
|
|
||||||
group 'hep.dataforge'
|
group 'hep.dataforge'
|
||||||
version '0.1.1-SNAPSHOT'
|
version '0.1.1-SNAPSHOT'
|
||||||
|
|
||||||
subprojects{
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
|
maven { url = "http://dl.bintray.com/kotlin/kotlin-eap" }
|
||||||
maven { url "https://kotlin.bintray.com/kotlinx" }
|
maven { url "https://kotlin.bintray.com/kotlinx" }
|
||||||
//maven { url 'https://jitpack.io' }
|
//maven { url 'https://jitpack.io' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
apply plugin: 'kotlinx-serialization'
|
||||||
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
plugins {
|
plugins{
|
||||||
id 'kotlin-platform-common'
|
id 'kotlin-platform-common'
|
||||||
id 'kotlinx-serialization'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -9,9 +8,3 @@ dependencies {
|
|||||||
testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common"
|
testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common"
|
||||||
testCompile "org.jetbrains.kotlin:kotlin-test-common"
|
testCompile "org.jetbrains.kotlin:kotlin-test-common"
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
|
||||||
experimental {
|
|
||||||
coroutines "enable"
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,6 @@ package hep.dataforge.meta
|
|||||||
|
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A member of the meta tree. Could be represented as one of following:
|
* A member of the meta tree. Could be represented as one of following:
|
||||||
@ -11,9 +10,34 @@ import kotlinx.serialization.Serializable
|
|||||||
* * a list of nodes
|
* * a list of nodes
|
||||||
*/
|
*/
|
||||||
sealed class MetaItem<M : Meta> {
|
sealed class MetaItem<M : Meta> {
|
||||||
class ValueItem<M : Meta>(val value: Value) : MetaItem<M>()
|
class ValueItem<M : Meta>(val value: Value) : MetaItem<M>(){
|
||||||
class SingleNodeItem<M : Meta>(val node: M) : MetaItem<M>()
|
override fun equals(other: Any?): Boolean {
|
||||||
class MultiNodeItem<M : Meta>(val nodes: List<M>) : MetaItem<M>()
|
return this.value == (other as? ValueItem<*>)?.value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return value.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class SingleNodeItem<M : Meta>(val node: M) : MetaItem<M>(){
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return this.node == (other as? SingleNodeItem<*>)?.node
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return node.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MultiNodeItem<M : Meta>(val nodes: List<M>) : MetaItem<M>(){
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return this.nodes == (other as? MultiNodeItem<*>)?.nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return nodes.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun <M : Meta> List<M>.get(query: String): M? {
|
operator fun <M : Meta> List<M>.get(query: String): M? {
|
||||||
@ -31,7 +55,6 @@ operator fun <M : Meta> List<M>.get(query: String): M? {
|
|||||||
* * [MetaItem.SingleNodeItem] single node
|
* * [MetaItem.SingleNodeItem] single node
|
||||||
* * [MetaItem.MultiNodeItem] multi-value node
|
* * [MetaItem.MultiNodeItem] multi-value node
|
||||||
*/
|
*/
|
||||||
@Serializable
|
|
||||||
interface Meta {
|
interface Meta {
|
||||||
val items: Map<String, MetaItem<out Meta>>
|
val items: Map<String, MetaItem<out Meta>>
|
||||||
}
|
}
|
||||||
@ -62,6 +85,22 @@ abstract class MetaNode<M : MetaNode<M>> : Meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
operator fun get(key: String): MetaItem<M>? = get(key.toName())
|
operator fun get(key: String): MetaItem<M>? = get(key.toName())
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is Meta) return false
|
||||||
|
|
||||||
|
return this.items == other.items
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return items.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return toJSON().toString()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,30 +108,75 @@ abstract class MetaNode<M : MetaNode<M>> : Meta {
|
|||||||
*
|
*
|
||||||
* If the argument is possibly mutable node, it is copied on creation
|
* If the argument is possibly mutable node, it is copied on creation
|
||||||
*/
|
*/
|
||||||
class SealedMeta(meta: Meta) : MetaNode<SealedMeta>() {
|
class SealedMeta internal constructor(override val items: Map<String, MetaItem<SealedMeta>>) : MetaNode<SealedMeta>() {
|
||||||
override val items: Map<String, MetaItem<SealedMeta>> = if (meta is SealedMeta) {
|
|
||||||
|
companion object {
|
||||||
|
fun seal(meta: Meta): SealedMeta {
|
||||||
|
val items = if (meta is SealedMeta) {
|
||||||
meta.items
|
meta.items
|
||||||
} else {
|
} else {
|
||||||
meta.items.mapValues { entry ->
|
meta.items.mapValues { entry ->
|
||||||
val item = entry.value
|
val item = entry.value
|
||||||
when (item) {
|
when (item) {
|
||||||
is MetaItem.ValueItem -> MetaItem.ValueItem(item.value)
|
is MetaItem.ValueItem -> MetaItem.ValueItem(item.value)
|
||||||
is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(SealedMeta(item.node))
|
is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(seal(item.node))
|
||||||
is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { SealedMeta(it) })
|
is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { seal(it) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return SealedMeta(items)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate sealed node from [this]. If it is already sealed return it as is
|
* Generate sealed node from [this]. If it is already sealed return it as is
|
||||||
*/
|
*/
|
||||||
fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(this)
|
fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta.seal(this)
|
||||||
|
|
||||||
object EmptyMeta : Meta {
|
object EmptyMeta : Meta {
|
||||||
override val items: Map<String, MetaItem<out Meta>> = emptyMap()
|
override val items: Map<String, MetaItem<out Meta>> = emptyMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsafe methods to access values and nodes directly from [MetaItem]
|
||||||
|
*/
|
||||||
|
|
||||||
|
val MetaItem<*>.value
|
||||||
|
get() = (this as? MetaItem.ValueItem)?.value ?: error("Trying to interpret node meta item as value item")
|
||||||
|
val MetaItem<*>.string get() = value.string
|
||||||
|
val MetaItem<*>.boolean get() = value.boolean
|
||||||
|
val MetaItem<*>.number get() = value.number
|
||||||
|
val MetaItem<*>.double get() = number.toDouble()
|
||||||
|
val MetaItem<*>.int get() = number.toInt()
|
||||||
|
val MetaItem<*>.long get() = number.toLong()
|
||||||
|
|
||||||
|
val <M : Meta> MetaItem<M>.node: M
|
||||||
|
get() = when (this) {
|
||||||
|
is MetaItem.ValueItem -> error("Trying to interpret value meta item as node item")
|
||||||
|
is MetaItem.SingleNodeItem -> node
|
||||||
|
is MetaItem.MultiNodeItem -> nodes.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility method to access item content as list of nodes.
|
||||||
|
* Returns empty list if it is value item.
|
||||||
|
*/
|
||||||
|
val <M : Meta> MetaItem<M>.nodes: List<M>
|
||||||
|
get() = when (this) {
|
||||||
|
is MetaItem.ValueItem -> emptyList()//error("Trying to interpret value meta item as node item")
|
||||||
|
is MetaItem.SingleNodeItem -> listOf(node)
|
||||||
|
is MetaItem.MultiNodeItem -> nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <M : Meta> MetaItem<M>.indexOf(meta: M): Int {
|
||||||
|
return when (this) {
|
||||||
|
is MetaItem.ValueItem -> -1
|
||||||
|
is MetaItem.SingleNodeItem -> if (node == meta) 0 else -1
|
||||||
|
is MetaItem.MultiNodeItem -> nodes.indexOf(meta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic meta-holder object
|
* Generic meta-holder object
|
||||||
*/
|
*/
|
||||||
|
@ -10,9 +10,20 @@ class MetaBuilder : MutableMetaNode<MetaBuilder>() {
|
|||||||
override fun empty(): MetaBuilder = MetaBuilder()
|
override fun empty(): MetaBuilder = MetaBuilder()
|
||||||
|
|
||||||
infix fun String.to(value: Any) {
|
infix fun String.to(value: Any) {
|
||||||
|
if (value is Meta) {
|
||||||
|
this@MetaBuilder[this] = value
|
||||||
|
}
|
||||||
this@MetaBuilder[this] = Value.of(value)
|
this@MetaBuilder[this] = Value.of(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
infix fun String.to(meta: Meta) {
|
||||||
|
this@MetaBuilder[this] = meta
|
||||||
|
}
|
||||||
|
|
||||||
|
infix fun String.to(value: Iterable<Meta>) {
|
||||||
|
this@MetaBuilder[this] = value.toList()
|
||||||
|
}
|
||||||
|
|
||||||
infix fun String.to(metaBuilder: MetaBuilder.() -> Unit) {
|
infix fun String.to(metaBuilder: MetaBuilder.() -> Unit) {
|
||||||
this@MetaBuilder[this] = MetaBuilder().apply(metaBuilder)
|
this@MetaBuilder[this] = MetaBuilder().apply(metaBuilder)
|
||||||
}
|
}
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
package hep.dataforge.meta
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unsafe methods to access values and nodes directly from [MetaItem]
|
|
||||||
*/
|
|
||||||
|
|
||||||
val MetaItem<*>.value
|
|
||||||
get() = (this as? MetaItem.ValueItem)?.value ?: error("Trying to interpret node meta item as value item")
|
|
||||||
val MetaItem<*>.string get() = value.string
|
|
||||||
val MetaItem<*>.boolean get() = value.boolean
|
|
||||||
val MetaItem<*>.number get() = value.number
|
|
||||||
val MetaItem<*>.double get() = number.toDouble()
|
|
||||||
val MetaItem<*>.int get() = number.toInt()
|
|
||||||
val MetaItem<*>.long get() = number.toLong()
|
|
||||||
|
|
||||||
val <M : Meta> MetaItem<M>.node: M
|
|
||||||
get() = when (this) {
|
|
||||||
is MetaItem.ValueItem -> error("Trying to interpret value meta item as node item")
|
|
||||||
is MetaItem.SingleNodeItem -> node
|
|
||||||
is MetaItem.MultiNodeItem -> nodes.first()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility method to access item content as list of nodes.
|
|
||||||
* Returns empty list if it is value item.
|
|
||||||
*/
|
|
||||||
val <M : Meta> MetaItem<M>.nodes: List<M>
|
|
||||||
get() = when (this) {
|
|
||||||
is MetaItem.ValueItem -> emptyList()//error("Trying to interpret value meta item as node item")
|
|
||||||
is MetaItem.SingleNodeItem -> listOf(node)
|
|
||||||
is MetaItem.MultiNodeItem -> nodes
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <M : Meta> MetaItem<M>.indexOf(meta: M): Int {
|
|
||||||
return when (this) {
|
|
||||||
is MetaItem.ValueItem -> -1
|
|
||||||
is MetaItem.SingleNodeItem -> if (node == meta) 0 else -1
|
|
||||||
is MetaItem.MultiNodeItem -> nodes.indexOf(meta)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +1,206 @@
|
|||||||
package hep.dataforge.meta
|
package hep.dataforge.meta
|
||||||
|
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.json.*
|
||||||
import kotlinx.serialization.internal.SerialClassDescImpl
|
|
||||||
|
|
||||||
@Serializer(forClass = Value::class)
|
|
||||||
object ValueSerializer : KSerializer<Value> {
|
|
||||||
override val serialClassDesc: KSerialClassDesc = SerialClassDescImpl("Value")
|
|
||||||
|
|
||||||
override fun load(input: KInput): Value {
|
/*Universal serialization*/
|
||||||
val key = input.readByteValue()
|
|
||||||
return when (key.toChar()) {
|
|
||||||
'S' -> StringValue(input.readStringValue())
|
|
||||||
'd' -> NumberValue(input.readDoubleValue())
|
|
||||||
'f' -> NumberValue(input.readFloatValue())
|
|
||||||
'i' -> NumberValue(input.readIntValue())
|
|
||||||
's' -> NumberValue(input.readShortValue())
|
|
||||||
'l' -> NumberValue(input.readLongValue())
|
|
||||||
'b' -> NumberValue(input.readByteValue())
|
|
||||||
'+' -> True
|
|
||||||
'-' -> False
|
|
||||||
'N' -> Null
|
|
||||||
'L' -> {
|
|
||||||
val size = input.readIntValue()
|
|
||||||
val list = (0 until size).map { load(input) }
|
|
||||||
ListValue(list)
|
|
||||||
}
|
|
||||||
else -> error("Unknown value deserialization ket '$key'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun save(output: KOutput, obj: Value) {
|
//sealed class MetaItemProxy {
|
||||||
when (obj.type) {
|
//
|
||||||
ValueType.NUMBER -> {
|
// @Serializable
|
||||||
val number = obj.number
|
// class NumberValueProxy(val number: Number) : MetaItemProxy()
|
||||||
when (number) {
|
//
|
||||||
is Float -> {
|
// @Serializable
|
||||||
output.writeByteValue('f'.toByte())
|
// class StringValueProxy(val string: String) : MetaItemProxy()
|
||||||
output.writeFloatValue(number)
|
//
|
||||||
|
// @Serializable
|
||||||
|
// class BooleanValueProxy(val boolean: Boolean) : MetaItemProxy()
|
||||||
|
//
|
||||||
|
// @Serializable
|
||||||
|
// object NullValueProxy : MetaItemProxy()
|
||||||
|
//
|
||||||
|
// @Serializable
|
||||||
|
// class MetaProxy(@Serializable val map: Map<String, MetaItemProxy>) : MetaItemProxy()
|
||||||
|
//
|
||||||
|
// @Serializable
|
||||||
|
// class MetaListProxy(@Serializable val nodes: List<MetaProxy>) : MetaItemProxy()
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//fun Meta.toMap(): Map<String, MetaItemProxy> {
|
||||||
|
// return this.items.mapValues { (_, value) ->
|
||||||
|
// when (value) {
|
||||||
|
// is MetaItem.ValueItem -> when (value.value.type) {
|
||||||
|
// ValueType.NUMBER -> MetaItemProxy.NumberValueProxy(value.value.number)
|
||||||
|
// ValueType.STRING -> MetaItemProxy.StringValueProxy(value.value.string)
|
||||||
|
// ValueType.BOOLEAN -> MetaItemProxy.BooleanValueProxy(value.value.boolean)
|
||||||
|
// ValueType.NULL -> MetaItemProxy.NullValueProxy
|
||||||
|
// }
|
||||||
|
// is MetaItem.SingleNodeItem -> MetaItemProxy.MetaProxy(value.node.toMap())
|
||||||
|
// is MetaItem.MultiNodeItem -> MetaItemProxy.MetaListProxy(value.nodes.map { MetaItemProxy.MetaProxy(it.toMap()) })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
/*Direct JSON serialization*/
|
||||||
|
|
||||||
|
fun Value.toJson(): JsonElement = if (isList()) {
|
||||||
|
JsonArray(list.map { it.toJson() })
|
||||||
|
} else {
|
||||||
|
when (type) {
|
||||||
|
ValueType.NUMBER -> JsonPrimitive(number)
|
||||||
|
ValueType.STRING -> JsonPrimitive(string)
|
||||||
|
ValueType.BOOLEAN -> JsonPrimitive(boolean)
|
||||||
|
ValueType.NULL -> JsonNull
|
||||||
}
|
}
|
||||||
is Short -> {
|
}
|
||||||
output.writeByteValue('s'.toByte())
|
|
||||||
output.writeShortValue(number)
|
fun Meta.toJSON(): JsonObject {
|
||||||
}
|
val map = this.items.mapValues { (_, value) ->
|
||||||
is Int -> {
|
when (value) {
|
||||||
output.writeByteValue('i'.toByte())
|
is MetaItem.ValueItem -> value.value.toJson()
|
||||||
output.writeIntValue(number)
|
is MetaItem.SingleNodeItem -> value.node.toJSON()
|
||||||
}
|
is MetaItem.MultiNodeItem -> JsonArray(value.nodes.map { it.toJSON() })
|
||||||
is Long -> {
|
|
||||||
output.writeByteValue('l'.toByte())
|
|
||||||
output.writeLongValue(number)
|
|
||||||
}
|
|
||||||
is Byte -> {
|
|
||||||
output.writeByteValue('b'.toByte())
|
|
||||||
output.writeByteValue(number)
|
|
||||||
}
|
|
||||||
is Double -> {
|
|
||||||
output.writeByteValue('d'.toByte())
|
|
||||||
output.writeDoubleValue(number)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
//TODO add warning
|
|
||||||
output.writeByteValue('d'.toByte())
|
|
||||||
output.writeDoubleValue(number.toDouble())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return JsonObject(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun JsonPrimitive.toValue(): Value {
|
||||||
|
return when (this) {
|
||||||
|
is JsonLiteral -> LazyParsedValue(content)
|
||||||
|
is JsonNull -> Null
|
||||||
}
|
}
|
||||||
ValueType.STRING -> {
|
}
|
||||||
output.writeByteValue('S'.toByte())
|
|
||||||
output.writeStringValue(obj.string)
|
fun JsonObject.toMeta(): Meta {
|
||||||
}
|
return buildMeta {
|
||||||
ValueType.BOOLEAN -> if (obj.boolean) {
|
this@toMeta.forEach { (key, value) ->
|
||||||
output.writeByteValue('+'.toByte())
|
when (value) {
|
||||||
|
is JsonPrimitive -> set(key, value.toValue())
|
||||||
|
is JsonObject -> set(key, value.toMeta())
|
||||||
|
is JsonArray -> if (value.all { it is JsonPrimitive }) {
|
||||||
|
set(key, ListValue(value.map { (it as JsonPrimitive).toValue() }))
|
||||||
} else {
|
} else {
|
||||||
output.writeByteValue('-'.toByte())
|
set(
|
||||||
|
key,
|
||||||
|
value.map {
|
||||||
|
if (it is JsonObject) {
|
||||||
|
it.toMeta()
|
||||||
|
} else {
|
||||||
|
buildMeta { "@value" to it.primitive.toValue() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ValueType.NULL -> output.writeByteValue('N'.toByte())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*Direct CBOR serialization*/
|
||||||
|
|
||||||
//@Serializer(forClass = Meta::class)
|
//fun Meta.toBinary(out: OutputStream) {
|
||||||
//object MetaSerializer: KSerializer<Meta>{
|
// fun CBOR.CBOREncoder.encodeChar(char: Char) {
|
||||||
// override val serialClassDesc: KSerialClassDesc = SerialClassDescImpl("Meta")
|
// encodeNumber(char.toByte().toLong())
|
||||||
//
|
|
||||||
// override fun load(input: KInput): Meta {
|
|
||||||
//
|
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// override fun save(output: KOutput, obj: Meta) {
|
// fun CBOR.CBOREncoder.encodeValue(value: Value) {
|
||||||
// NamedValueOutput()
|
// if (value.isList()) {
|
||||||
|
// encodeChar('L')
|
||||||
|
// startArray()
|
||||||
|
// value.list.forEach {
|
||||||
|
// encodeValue(it)
|
||||||
|
// }
|
||||||
|
// end()
|
||||||
|
// } else when (value.type) {
|
||||||
|
// ValueType.NUMBER -> when (value.value) {
|
||||||
|
// is Int, is Short, is Long -> {
|
||||||
|
// encodeChar('i')
|
||||||
|
// encodeNumber(value.number.toLong())
|
||||||
|
// }
|
||||||
|
// is Float -> {
|
||||||
|
// encodeChar('f')
|
||||||
|
// encodeFloat(value.number.toFloat())
|
||||||
|
// }
|
||||||
|
// else -> {
|
||||||
|
// encodeChar('d')
|
||||||
|
// encodeDouble(value.number.toDouble())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ValueType.STRING -> {
|
||||||
|
// encodeChar('S')
|
||||||
|
// encodeString(value.string)
|
||||||
|
// }
|
||||||
|
// ValueType.BOOLEAN -> {
|
||||||
|
// if (value.boolean) {
|
||||||
|
// encodeChar('+')
|
||||||
|
// } else {
|
||||||
|
// encodeChar('-')
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ValueType.NULL -> {
|
||||||
|
// encodeChar('N')
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fun CBOR.CBOREncoder.encodeMeta(meta: Meta) {
|
||||||
|
// meta.items.forEach { (key, item) ->
|
||||||
|
// this.startMap()
|
||||||
|
// encodeString(key)
|
||||||
|
// when (item) {
|
||||||
|
// is MetaItem.ValueItem -> {
|
||||||
|
// encodeChar('V')
|
||||||
|
// encodeValue(item.value)
|
||||||
|
// }
|
||||||
|
// is MetaItem.SingleNodeItem -> {
|
||||||
|
// startArray()
|
||||||
|
// encodeMeta(item.node)
|
||||||
|
// }
|
||||||
|
// is MetaItem.MultiNodeItem -> {
|
||||||
|
// startArray()
|
||||||
|
// item.nodes.forEach {
|
||||||
|
// encodeMeta(it)
|
||||||
|
// }
|
||||||
|
// end()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// CBOR.CBOREncoder(out).apply {
|
||||||
|
// encodeMeta(this@toBinary)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//fun InputStream.readBinaryMeta(): Meta {
|
||||||
|
// fun CBOR.CBORDecoder.nextChar(): Char = nextNumber().toByte().toChar()
|
||||||
|
//
|
||||||
|
// fun CBOR.CBORDecoder.nextValue(): Value {
|
||||||
|
// val key = nextChar()
|
||||||
|
// return when(key){
|
||||||
|
// 'L' -> {
|
||||||
|
// val size = startArray()
|
||||||
|
// val res = (0 until size).map { nextValue() }
|
||||||
|
// end()
|
||||||
|
// ListValue(res)
|
||||||
|
// }
|
||||||
|
// 'S' -> StringValue(nextString())
|
||||||
|
// 'N' -> Null
|
||||||
|
// '+' -> True
|
||||||
|
// '-' -> False
|
||||||
|
// 'i' -> NumberValue(nextNumber())
|
||||||
|
// 'f' -> NumberValue(nextFloat())
|
||||||
|
// 'd' -> NumberValue(nextDouble())
|
||||||
|
// else -> error("Unknown binary key: $key")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fun CBOR.CBORDecoder.nextMeta(): Meta{
|
||||||
|
//
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
//}
|
//}
|
@ -1,7 +1,5 @@
|
|||||||
package hep.dataforge.meta
|
package hep.dataforge.meta
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of supported Value types.
|
* The list of supported Value types.
|
||||||
@ -18,7 +16,6 @@ enum class ValueType {
|
|||||||
*
|
*
|
||||||
* Value can represent a list of value objects.
|
* Value can represent a list of value objects.
|
||||||
*/
|
*/
|
||||||
@Serializable(with = ValueSerializer::class)
|
|
||||||
interface Value {
|
interface Value {
|
||||||
/**
|
/**
|
||||||
* Get raw value of this value
|
* Get raw value of this value
|
||||||
@ -69,10 +66,10 @@ interface Value {
|
|||||||
* A singleton null value
|
* A singleton null value
|
||||||
*/
|
*/
|
||||||
object Null : Value {
|
object Null : Value {
|
||||||
override val value: Any? = null
|
override val value: Any? get() = null
|
||||||
override val type: ValueType = ValueType.NULL
|
override val type: ValueType get() = ValueType.NULL
|
||||||
override val number: Number = Double.NaN
|
override val number: Number get() = Double.NaN
|
||||||
override val string: String = "@null"
|
override val string: String get() = "@null"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,40 +82,60 @@ fun Value.isNull(): Boolean = this == Null
|
|||||||
* Singleton true value
|
* Singleton true value
|
||||||
*/
|
*/
|
||||||
object True : Value {
|
object True : Value {
|
||||||
override val value: Any? = true
|
override val value: Any? get() = true
|
||||||
override val type: ValueType = ValueType.BOOLEAN
|
override val type: ValueType get() = ValueType.BOOLEAN
|
||||||
override val number: Number = 1.0
|
override val number: Number get() = 1.0
|
||||||
override val string: String = "+"
|
override val string: String get() = "+"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton false value
|
* Singleton false value
|
||||||
*/
|
*/
|
||||||
object False : Value {
|
object False : Value {
|
||||||
override val value: Any? = false
|
override val value: Any? get() = false
|
||||||
override val type: ValueType = ValueType.BOOLEAN
|
override val type: ValueType get() = ValueType.BOOLEAN
|
||||||
override val number: Number = -1.0
|
override val number: Number get() = -1.0
|
||||||
override val string: String = "-"
|
override val string: String get() = "-"
|
||||||
}
|
}
|
||||||
|
|
||||||
val Value.boolean get() = this == True || this.list.firstOrNull() == True || (type == ValueType.STRING && string.toBoolean())
|
val Value.boolean get() = this == True || this.list.firstOrNull() == True || (type == ValueType.STRING && string.toBoolean())
|
||||||
|
|
||||||
class NumberValue(override val number: Number) : Value {
|
class NumberValue(override val number: Number) : Value {
|
||||||
override val value: Any? get() = number
|
override val value: Any? get() = number
|
||||||
override val type: ValueType = ValueType.NUMBER
|
override val type: ValueType get() = ValueType.NUMBER
|
||||||
override val string: String get() = number.toString()
|
override val string: String get() = number.toString()
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return this.number == (other as? Value)?.number
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int = number.hashCode()
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class StringValue(override val string: String) : Value {
|
class StringValue(override val string: String) : Value {
|
||||||
override val value: Any? get() = string
|
override val value: Any? get() = string
|
||||||
override val type: ValueType = ValueType.STRING
|
override val type: ValueType get() = ValueType.STRING
|
||||||
override val number: Number get() = string.toDouble()
|
override val number: Number get() = string.toDouble()
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return this.string == (other as? Value)?.string
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int = string.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
class EnumValue<E : Enum<*>>(override val value: E) : Value {
|
class EnumValue<E : Enum<*>>(override val value: E) : Value {
|
||||||
override val type: ValueType = ValueType.STRING
|
override val type: ValueType get() = ValueType.STRING
|
||||||
override val number: Number = value.ordinal
|
override val number: Number get() = value.ordinal
|
||||||
override val string: String = value.name
|
override val string: String get() = value.name
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return string == (other as? Value)?.string
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int = value.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
class ListValue(override val list: List<Value>) : Value {
|
class ListValue(override val list: List<Value>) : Value {
|
||||||
@ -148,6 +165,7 @@ fun String.asValue(): Value = StringValue(this)
|
|||||||
|
|
||||||
fun Collection<Value>.asValue(): Value = ListValue(this.toList())
|
fun Collection<Value>.asValue(): Value = ListValue(this.toList())
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create Value from String using closest match conversion
|
* Create Value from String using closest match conversion
|
||||||
*/
|
*/
|
||||||
@ -182,3 +200,14 @@ fun String.parseValue(): Value {
|
|||||||
//Give up and return a StringValue
|
//Give up and return a StringValue
|
||||||
return StringValue(this)
|
return StringValue(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LazyParsedValue(override val string: String): Value{
|
||||||
|
private val parsedValue by lazy { string.parseValue() }
|
||||||
|
|
||||||
|
override val value: Any?
|
||||||
|
get() = parsedValue.value
|
||||||
|
override val type: ValueType
|
||||||
|
get() = parsedValue.type
|
||||||
|
override val number: Number
|
||||||
|
get() = parsedValue.number
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package hep.dataforge.names
|
package hep.dataforge.names
|
||||||
|
|
||||||
import kotlin.coroutines.experimental.buildSequence
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The general interface for working with names.
|
* The general interface for working with names.
|
||||||
@ -75,7 +74,7 @@ data class NameToken internal constructor(val body: String, val query: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun String.toName(): Name {
|
fun String.toName(): Name {
|
||||||
val tokens = buildSequence<NameToken> {
|
val tokens = sequence {
|
||||||
var bodyBuilder = StringBuilder()
|
var bodyBuilder = StringBuilder()
|
||||||
var queryBuilder = StringBuilder()
|
var queryBuilder = StringBuilder()
|
||||||
var bracketCount: Int = 0
|
var bracketCount: Int = 0
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
package scientifik.kplot.remote
|
package hep.dataforge.meta
|
||||||
|
|
||||||
import hep.dataforge.meta.buildMeta
|
|
||||||
import hep.dataforge.meta.get
|
|
||||||
import hep.dataforge.meta.value
|
|
||||||
import kotlinx.serialization.json.JSON
|
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class SerializationTest {
|
||||||
|
|
||||||
class SerializationTest{
|
|
||||||
@Test
|
@Test
|
||||||
fun testMetaSerialization(){
|
fun testJSONSerialization() {
|
||||||
val meta = buildMeta {
|
val meta = buildMeta {
|
||||||
"a" to 2
|
"a" to 2
|
||||||
"b" to {
|
"b" to {
|
||||||
@ -16,7 +14,39 @@ class SerializationTest{
|
|||||||
"d" to 2.2
|
"d" to 2.2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val json = JSON.stringify(meta["a"]?.value!!)
|
val json = meta.toJSON()
|
||||||
println(json)
|
println(json)
|
||||||
|
val result = json.toMeta()
|
||||||
|
assertEquals(meta, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// fun testIndirectSerialization() {
|
||||||
|
// val meta = buildMeta {
|
||||||
|
// "a" to 2
|
||||||
|
// "b" to {
|
||||||
|
// "c" to "ddd"
|
||||||
|
// "d" to 2.2
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// val json = JSON.stringify(meta.toMap())
|
||||||
|
// println(json)
|
||||||
|
//// val result = json.toMeta()
|
||||||
|
//// assertEquals(meta, result)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// fun testWeirdSerialization() {
|
||||||
|
// val meta = buildMeta {
|
||||||
|
// "a" to 2
|
||||||
|
// "b" to {
|
||||||
|
// "c" to "ddd"
|
||||||
|
// "d" to 2.2
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// val json = JSON.stringify(meta.toJSON())
|
||||||
|
// println(json)
|
||||||
|
// val result: JsonObject = JSON.parse(json)
|
||||||
|
// assertEquals(meta, result.toMeta())
|
||||||
|
// }
|
||||||
}
|
}
|
@ -1,6 +1,5 @@
|
|||||||
plugins {
|
plugins{
|
||||||
id 'kotlin-platform-js'
|
id 'kotlin-platform-js'
|
||||||
//id 'kotlinx-serialization'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
plugins {
|
plugins{
|
||||||
id 'kotlin-platform-jvm'
|
id 'kotlin-platform-jvm'
|
||||||
id 'kotlinx-serialization'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
expectedBy project(":dataforge-meta-common")
|
expectedBy project(":dataforge-meta-common")
|
||||||
|
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
|
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||||
compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"
|
compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"
|
||||||
testCompile "org.jetbrains.kotlin:kotlin-test"
|
testCompile "org.jetbrains.kotlin:kotlin-test"
|
||||||
testCompile "org.jetbrains.kotlin:kotlin-test-junit5"
|
testCompile "org.jetbrains.kotlin:kotlin-test-junit"
|
||||||
}
|
}
|
||||||
|
|
||||||
compileKotlin {
|
compileKotlin {
|
||||||
|
Loading…
Reference in New Issue
Block a user