Meta to JSON SNS fix

This commit is contained in:
Alexander Nozik 2020-03-28 10:04:59 +03:00
parent 66cee2d42b
commit 7f0ad68d9d
6 changed files with 131 additions and 35 deletions

View File

@ -1,9 +1,11 @@
package hep.dataforge.data package hep.dataforge.data
import hep.dataforge.meta.* import hep.dataforge.meta.Meta
import hep.dataforge.meta.isEmpty
import hep.dataforge.meta.scheme.Scheme import hep.dataforge.meta.scheme.Scheme
import hep.dataforge.meta.scheme.SchemeSpec import hep.dataforge.meta.scheme.SchemeSpec
import hep.dataforge.meta.scheme.string import hep.dataforge.meta.scheme.string
import hep.dataforge.meta.scheme.wrap
import hep.dataforge.names.toName import hep.dataforge.names.toName
@ -55,4 +57,4 @@ fun <T : Any> DataNode<T>.filter(filter: Meta): DataNode<T> = filter(DataFilter.
* Filter data using [DataFilter] builder * Filter data using [DataFilter] builder
*/ */
fun <T : Any> DataNode<T>.filter(filterBuilder: DataFilter.() -> Unit): DataNode<T> = fun <T : Any> DataNode<T>.filter(filterBuilder: DataFilter.() -> Unit): DataNode<T> =
filter(DataFilter.invoke(filterBuilder)) filter(DataFilter(filterBuilder))

View File

@ -3,6 +3,7 @@ package hep.dataforge.meta
import hep.dataforge.meta.descriptors.ItemDescriptor import hep.dataforge.meta.descriptors.ItemDescriptor
import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.ValueDescriptor import hep.dataforge.meta.descriptors.ValueDescriptor
import hep.dataforge.meta.scheme.getProperty
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.values.* import hep.dataforge.values.*
@ -26,27 +27,75 @@ fun Value.toJson(descriptor: ValueDescriptor? = null): JsonElement {
} }
//Use these methods to customize JSON key mapping //Use these methods to customize JSON key mapping
private fun NameToken.toJsonKey(descriptor: ItemDescriptor?) = toString() private fun String.toJsonKey(descriptor: ItemDescriptor?) = descriptor?.getProperty("jsonName").string ?: toString()
//private fun NodeDescriptor?.getDescriptor(key: String) = this?.items?.get(key) //private fun NodeDescriptor?.getDescriptor(key: String) = this?.items?.get(key)
fun Meta.toJson(descriptor: NodeDescriptor? = null): JsonObject { /**
* Convert given [Meta] to [JsonObject]. Primitives and nodes are copied as is, same name siblings are treated as json arrays
*/
fun Meta.toJson(descriptor: NodeDescriptor? = null, index: String? = null): JsonObject {
//TODO search for same name siblings and arrange them into arrays val elementMap = HashMap<String, JsonElement>()
val map = this.items.entries.associate { (name, item) ->
val itemDescriptor = descriptor?.items?.get(name.body) fun MetaItem<*>.toJsonElement(itemDescriptor: ItemDescriptor?, index: String? = null): JsonElement = when (this) {
val key = name.toJsonKey(itemDescriptor) is MetaItem.ValueItem -> {
val value = when (item) { value.toJson(itemDescriptor as? ValueDescriptor)
is MetaItem.ValueItem -> { }
item.value.toJson(itemDescriptor as? ValueDescriptor) is MetaItem.NodeItem -> {
node.toJson(itemDescriptor as? NodeDescriptor, index)
}
}
fun addElement(key: String) {
val itemDescriptor = descriptor?.items?.get(key)
val jsonKey = key.toJsonKey(itemDescriptor)
val items = getIndexed(key)
when (items.size) {
0 -> {
//do nothing
} }
is MetaItem.NodeItem -> { 1 -> {
item.node.toJson(itemDescriptor as? NodeDescriptor) elementMap[jsonKey] = items.values.first().toJsonElement(itemDescriptor)
}
else -> {
val array = jsonArray {
items.forEach { (index, item) ->
+item.toJsonElement(itemDescriptor, index)
}
}
elementMap[jsonKey] = array
} }
} }
key to value
} }
return JsonObject(map)
((descriptor?.items?.keys ?: emptySet()) + items.keys.map { it.body }).forEach(::addElement)
if (index != null) {
elementMap["@index"] = JsonPrimitive(index)
}
return JsonObject(elementMap)
// // use descriptor keys in the order they are declared
// val keys = (descriptor?.items?.keys ?: emptySet()) + this.items.keys.map { it.body }
//
// //TODO search for same name siblings and arrange them into arrays
// val map = this.items.entries.associate { (name, item) ->
// val itemDescriptor = descriptor?.items?.get(name.body)
// val key = name.toJsonKey(itemDescriptor)
// val value = when (item) {
// is MetaItem.ValueItem -> {
// item.value.toJson(itemDescriptor as? ValueDescriptor)
// }
// is MetaItem.NodeItem -> {
// item.node.toJson(itemDescriptor as? NodeDescriptor)
// }
// }
// key to value
// }
// return JsonObject(map)
} }
fun JsonObject.toMeta(descriptor: NodeDescriptor? = null): Meta = JsonMeta(this, descriptor) fun JsonObject.toMeta(descriptor: NodeDescriptor? = null): Meta = JsonMeta(this, descriptor)
@ -88,6 +137,9 @@ fun JsonElement.toMetaItem(descriptor: ItemDescriptor? = null): MetaItem<JsonMet
} }
} }
/**
* A meta wrapping json object
*/
class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : MetaBase() { class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : MetaBase() {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@ -95,7 +147,8 @@ class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : M
val itemDescriptor = descriptor?.items?.get(key) val itemDescriptor = descriptor?.items?.get(key)
return when (value) { return when (value) {
is JsonPrimitive -> { is JsonPrimitive -> {
this[key] = MetaItem.ValueItem(value.toValue(itemDescriptor as? ValueDescriptor)) as MetaItem<JsonMeta> this[key] =
MetaItem.ValueItem(value.toValue(itemDescriptor as? ValueDescriptor)) as MetaItem<JsonMeta>
} }
is JsonObject -> { is JsonObject -> {
this[key] = MetaItem.NodeItem( this[key] = MetaItem.NodeItem(
@ -125,7 +178,7 @@ class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : M
} }
override val items: Map<NameToken, MetaItem<JsonMeta>> by lazy { override val items: Map<NameToken, MetaItem<JsonMeta>> by lazy {
val map = HashMap<String, MetaItem<JsonMeta>>() val map = LinkedHashMap<String, MetaItem<JsonMeta>>()
json.forEach { (key, value) -> map[key] = value } json.forEach { (key, value) -> map[key] = value }
map.mapKeys { it.key.toName().first()!! } map.mapKeys { it.key.toName().first()!! }
} }

View File

@ -17,7 +17,7 @@ interface MutableMeta<out M : MutableMeta<M>> : MetaNode<M> {
* Changes in Meta are not thread safe. * Changes in Meta are not thread safe.
*/ */
abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(), MutableMeta<M> { abstract class AbstractMutableMeta<M : MutableMeta<M>> : AbstractMetaNode<M>(), MutableMeta<M> {
protected val _items: MutableMap<NameToken, MetaItem<M>> = HashMap() protected val _items: MutableMap<NameToken, MetaItem<M>> = LinkedHashMap()
override val items: Map<NameToken, MetaItem<M>> override val items: Map<NameToken, MetaItem<M>>
get() = _items get() = _items

View File

@ -68,6 +68,9 @@ open class SchemeSpec<T : Scheme>(val builder: () -> T) : Specification<T> {
this.defaultProvider = defaultProvider this.defaultProvider = defaultProvider
} }
} }
@Suppress("OVERRIDE_BY_INLINE")
final override inline operator fun invoke(action: T.() -> Unit) = empty().apply(action)
} }
/** /**

View File

@ -10,13 +10,6 @@ import kotlin.jvm.JvmName
* *
*/ */
interface Specification<T : Configurable> { interface Specification<T : Configurable> {
/**
* Update given configuration using given type as a builder
*/
fun update(config: Config, action: T.() -> Unit): T {
return wrap(config).apply(action)
}
fun empty() = wrap() fun empty() = wrap()
/** /**
@ -24,18 +17,24 @@ interface Specification<T : Configurable> {
*/ */
fun wrap(config: Config = Config(), defaultProvider: (Name) -> MetaItem<*>? = { null }): T fun wrap(config: Config = Config(), defaultProvider: (Name) -> MetaItem<*>? = { null }): T
/** operator fun invoke(action: T.() -> Unit) = empty().apply(action)
* Wrap a configuration using static meta as default
*/
fun wrap(config: Config = Config(), default: Meta): T = wrap(config) { default[it] }
/**
* Wrap a configuration using static meta as default
*/
fun wrap(default: Meta): T = wrap(Config()) { default[it] }
} }
inline operator fun <T : Configurable> Specification<T>.invoke(action: T.() -> Unit) = empty().apply(action) /**
* Update given configuration using given type as a builder
*/
fun <T : Configurable> Specification<T>.update(config: Config, action: T.() -> Unit): T = wrap(config).apply(action)
/**
* Wrap a configuration using static meta as default
*/
fun <T : Configurable> Specification<T>.wrap(config: Config = Config(), default: Meta): T = wrap(config) { default[it] }
/**
* Wrap a configuration using static meta as default
*/
fun <T : Configurable> Specification<T>.wrap(default: Meta): T = wrap(Config()) { default[it] }
/** /**
* Apply specified configuration to configurable * Apply specified configuration to configurable

View File

@ -0,0 +1,39 @@
package hep.dataforge.meta
import kotlinx.serialization.json.int
import kotlinx.serialization.json.json
import kotlinx.serialization.json.jsonArray
import kotlin.test.Test
import kotlin.test.assertEquals
class JsonMetaTest {
val json = json {
"firstValue" to "a"
"secondValue" to "b"
"array" to jsonArray {
+"1"
+"2"
+"3"
}
"nodeArray" to jsonArray {
+json {
"index" to 1
}
+json {
"index" to 2
}
+json {
"index" to 3
}
}
}
@Test
fun jsonMetaConversion() {
val meta = json.toMeta()
val reconstructed = meta.toJson()
println(json)
println(reconstructed)
assertEquals(2, reconstructed["nodeArray"]?.jsonArray?.get(1)?.jsonObject?.get("index")?.int)
}
}