Meta to JSON SNS fix
This commit is contained in:
parent
66cee2d42b
commit
7f0ad68d9d
@ -1,9 +1,11 @@
|
||||
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.SchemeSpec
|
||||
import hep.dataforge.meta.scheme.string
|
||||
import hep.dataforge.meta.scheme.wrap
|
||||
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
|
||||
*/
|
||||
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.NodeDescriptor
|
||||
import hep.dataforge.meta.descriptors.ValueDescriptor
|
||||
import hep.dataforge.meta.scheme.getProperty
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.values.*
|
||||
@ -26,27 +27,75 @@ fun Value.toJson(descriptor: ValueDescriptor? = null): JsonElement {
|
||||
}
|
||||
|
||||
//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)
|
||||
|
||||
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 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)
|
||||
val elementMap = HashMap<String, JsonElement>()
|
||||
|
||||
fun MetaItem<*>.toJsonElement(itemDescriptor: ItemDescriptor?, index: String? = null): JsonElement = when (this) {
|
||||
is MetaItem.ValueItem -> {
|
||||
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 -> {
|
||||
item.node.toJson(itemDescriptor as? NodeDescriptor)
|
||||
1 -> {
|
||||
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)
|
||||
@ -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() {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@ -95,7 +147,8 @@ class JsonMeta(val json: JsonObject, val descriptor: NodeDescriptor? = null) : M
|
||||
val itemDescriptor = descriptor?.items?.get(key)
|
||||
return when (value) {
|
||||
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 -> {
|
||||
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 {
|
||||
val map = HashMap<String, MetaItem<JsonMeta>>()
|
||||
val map = LinkedHashMap<String, MetaItem<JsonMeta>>()
|
||||
json.forEach { (key, value) -> map[key] = value }
|
||||
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.
|
||||
*/
|
||||
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>>
|
||||
get() = _items
|
||||
|
@ -68,6 +68,9 @@ open class SchemeSpec<T : Scheme>(val builder: () -> T) : Specification<T> {
|
||||
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> {
|
||||
/**
|
||||
* 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()
|
||||
|
||||
/**
|
||||
@ -24,18 +17,24 @@ interface Specification<T : Configurable> {
|
||||
*/
|
||||
fun wrap(config: Config = Config(), defaultProvider: (Name) -> MetaItem<*>? = { null }): T
|
||||
|
||||
/**
|
||||
* 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] }
|
||||
operator fun invoke(action: T.() -> Unit) = empty().apply(action)
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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