Meta to JSON SNS fix
This commit is contained in:
parent
66cee2d42b
commit
7f0ad68d9d
@ -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))
|
@ -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()!! }
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user