Compatibility for Json top level arrays and nested arrays added.

This commit is contained in:
Alexander Nozik 2019-08-16 15:08:56 +03:00
parent 8c32eaeb6b
commit 58a0584cae
5 changed files with 137 additions and 100 deletions

View File

@ -1,75 +0,0 @@
/*
* Copyright 2015 Alexander Nozik.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package hep.dataforge.data
import hep.dataforge.meta.Meta
import hep.dataforge.meta.get
import hep.dataforge.meta.string
interface GroupRule {
operator fun <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>>
}
/**
* The class to builder groups of content with annotation defined rules
*
* @author Alexander Nozik
*/
object GroupBuilder {
/**
* Create grouping rule that creates groups for different values of value
* field with name [key]
*
* @param key
* @param defaultTagValue
* @return
*/
fun byValue(key: String, defaultTagValue: String): GroupRule = object :
GroupRule {
override fun <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>> {
val map = HashMap<String, DataTreeBuilder<T>>()
node.dataSequence().forEach { (name, data) ->
val tagValue = data.meta[key]?.string ?: defaultTagValue
map.getOrPut(tagValue) { DataNode.builder(node.type) }[name] = data
}
return map.mapValues { it.value.build() }
}
}
// @ValueDef(key = "byValue", required = true, info = "The name of annotation value by which grouping should be made")
// @ValueDef(
// key = "defaultValue",
// def = "default",
// info = "Default value which should be used for content in which the grouping value is not presented"
// )
fun byMeta(config: Meta): GroupRule {
//TODO expand grouping options
return config["byValue"]?.string?.let {
byValue(
it,
config["defaultValue"]?.string ?: "default"
)
}
?: object : GroupRule {
override fun <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>> = mapOf("" to node)
}
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2015 Alexander Nozik.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package hep.dataforge.data
import hep.dataforge.meta.Meta
import hep.dataforge.meta.get
import hep.dataforge.meta.string
interface GroupRule {
operator fun <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>>
companion object{
/**
* Create grouping rule that creates groups for different values of value
* field with name [key]
*
* @param key
* @param defaultTagValue
* @return
*/
fun byValue(key: String, defaultTagValue: String): GroupRule = object :
GroupRule {
override fun <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>> {
val map = HashMap<String, DataTreeBuilder<T>>()
node.dataSequence().forEach { (name, data) ->
val tagValue = data.meta[key]?.string ?: defaultTagValue
map.getOrPut(tagValue) { DataNode.builder(node.type) }[name] = data
}
return map.mapValues { it.value.build() }
}
}
// @ValueDef(key = "byValue", required = true, info = "The name of annotation value by which grouping should be made")
// @ValueDef(
// key = "defaultValue",
// def = "default",
// info = "Default value which should be used for content in which the grouping value is not presented"
// )
fun byMeta(config: Meta): GroupRule {
//TODO expand grouping options
return config["byValue"]?.string?.let {
byValue(
it,
config["defaultValue"]?.string ?: "default"
)
}
?: object : GroupRule {
override fun <T : Any> invoke(node: DataNode<T>): Map<String, DataNode<T>> = mapOf("" to node)
}
}
}
}

View File

@ -25,8 +25,8 @@ object JsonMetaFormat : MetaFormat {
override val key: Short = 0x4a53//"JS" override val key: Short = 0x4a53//"JS"
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) { override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) {
val str = meta.toJson().toString() val json = meta.toJson(descriptor)
writeText(str) writeText(json.toString())
} }
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta { override fun Input.readMeta(descriptor: NodeDescriptor?): Meta {
@ -78,17 +78,46 @@ fun Meta.toJson(descriptor: NodeDescriptor? = null): JsonObject {
return JsonObject(map) return JsonObject(map)
} }
fun JsonObject.toMeta(descriptor: NodeDescriptor? = null): JsonMeta = JsonMeta(this, descriptor)
fun JsonObject.toMeta(descriptor: NodeDescriptor? = null) = JsonMeta(this, descriptor) fun JsonPrimitive.toValue(descriptor: ValueDescriptor?): Value {
return when (this) {
JsonNull -> Null
else -> this.content.parseValue() // Optimize number and boolean parsing
}
}
class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : MetaBase() { fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMeta> = when (this) {
is JsonPrimitive -> {
private fun JsonPrimitive.toValue(descriptor: ValueDescriptor?): Value { val value = this.toValue(descriptor as? ValueDescriptor)
return when (this) { MetaItem.ValueItem(value)
JsonNull -> Null }
else -> this.content.parseValue() // Optimize number and boolean parsing is JsonObject -> {
val meta = toMeta(descriptor as? NodeDescriptor)
MetaItem.NodeItem(meta)
}
is JsonArray -> {
if (this.all { it is JsonPrimitive }) {
val value = if (isEmpty()) {
Null
} else {
ListValue(
map<JsonElement, Value> {
//We already checked that all values are primitives
(it as JsonPrimitive).toValue(descriptor as? ValueDescriptor)
}
)
}
MetaItem.ValueItem(value)
} else {
json {
"@value" to this@toMetaItem
}.toMetaItem(descriptor)
} }
} }
}
class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : MetaBase() {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private operator fun MutableMap<String, MetaItem<JsonMeta>>.set(key: String, value: JsonElement): Unit { private operator fun MutableMap<String, MetaItem<JsonMeta>>.set(key: String, value: JsonElement): Unit {
@ -114,17 +143,7 @@ class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : M
this[name] = MetaItem.ValueItem(listValue) as MetaItem<JsonMeta> this[name] = MetaItem.ValueItem(listValue) as MetaItem<JsonMeta>
} }
else -> value.forEachIndexed { index, jsonElement -> else -> value.forEachIndexed { index, jsonElement ->
when (jsonElement) { this["$name[$index]"] = jsonElement.toMetaItem(itemDescriptor)
is JsonObject -> {
this["$name[$index]"] =
MetaItem.NodeItem(jsonElement.toMeta(itemDescriptor as? NodeDescriptor))
}
is JsonPrimitive -> {
this["$name[$index]"] =
MetaItem.ValueItem(jsonElement.toValue(itemDescriptor as? ValueDescriptor))
}
is JsonArray -> TODO("Nested arrays not supported")
}
} }
} }
} }

View File

@ -29,7 +29,7 @@ interface MetaFormat : IOFormat<Meta>, Named {
} }
} }
fun Meta.toString(format: MetaFormat = JsonMetaFormat): String = buildPacket { fun Meta.toString(format: MetaFormat): String = buildPacket {
format.run { writeThis(this@toString) } format.run { writeThis(this@toString) }
}.readText() }.readText()

View File

@ -1,9 +1,9 @@
package hep.dataforge.io package hep.dataforge.io
import hep.dataforge.meta.Meta import hep.dataforge.meta.*
import hep.dataforge.meta.buildMeta import kotlinx.serialization.json.JsonPrimitive
import hep.dataforge.meta.get import kotlinx.serialization.json.json
import hep.dataforge.meta.seal import kotlinx.serialization.json.jsonArray
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -45,4 +45,29 @@ class MetaFormatTest {
assertEquals<Meta>(meta, result) assertEquals<Meta>(meta, result)
} }
@Test
fun testJsonToMeta(){
val json = jsonArray{
//top level array
+jsonArray {
+JsonPrimitive(88)
+json{
"c" to "aasdad"
"d" to true
}
}
+"value"
+jsonArray {
+JsonPrimitive(1.0)
+JsonPrimitive(2.0)
+JsonPrimitive(3.0)
}
}
val meta = json.toMetaItem().node!!
assertEquals(true, meta["@value[0].@value[1].d"].boolean)
assertEquals("value", meta["@value[1]"].string)
assertEquals(listOf(1.0,2.0,3.0),meta["@value[2"].value?.list?.map{it.number.toDouble()})
}
} }