Skip to content

Instantly share code, notes, and snippets.

@CattenLinger
Created December 26, 2018 19:24
Show Gist options
  • Save CattenLinger/b92d35dde1d5e15113d40ae01ba7fa63 to your computer and use it in GitHub Desktop.
Save CattenLinger/b92d35dde1d5e15113d40ae01ba7fa63 to your computer and use it in GitHub Desktop.
JsonCat - Tool for json flatten
package net.catten.json.huge.extractor.complex
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.node.JsonNodeType
import java.util.*
import kotlin.collections.ArrayList
class JsonCat {
fun extractJson(jsonNode: JsonNode): List<JsonKnot> {
val context = ProcessContext(Stack())
context.stack.push(Pair("", jsonNode))
extract(context)
return context.result
}
data class ProcessContext(val stack: Stack<Pair<String, JsonNode>>,
val result: ArrayList<JsonKnot> = ArrayList())
private fun extract(context: ProcessContext) {
val stack = context.stack
val latest = stack.peek()
val node = latest.second
val currentPathInfo = stack.map { it.first }.toCollection(LinkedList())
when (node.nodeType) {
// If node is object, go deep
JsonNodeType.OBJECT -> node.fields().forEach {
stack.push(Pair(it.key, it.value))
extract(context)
}
// if node is array, and content is pure value, put them to result
// if is object, go deeper
JsonNodeType.ARRAY -> if (node.size() != 0) {
if (node[0].isValueNode) node.elements().asSequence().forEachIndexed { index, jsonNode ->
context.result.add(JsonKnot(jsonNode, stack.map { it.first }.toCollection(LinkedList()).apply {
add("$index")
}))
stack.pop()
return
} else node.elements().asSequence().forEachIndexed { index, jsonNode ->
stack.push(Pair("$index", jsonNode))
extract(context)
}
}
// If node is pure value, add it to result
else -> {
context.result.add(JsonKnot(node, currentPathInfo))
stack.pop()
return
}
}
// add current node to result
context.result.add(JsonKnot(node, currentPathInfo))
stack.pop()
}
}
class JsonKnot {
val jsonNode: JsonNode
val path: LinkedList<String>
val type: JsonNodeType
get() = jsonNode.nodeType
val pathName: String
constructor(jsonNode: JsonNode, path: LinkedList<String>) {
this.jsonNode = jsonNode
this.path = path
this.pathName = buildPathName()
}
/*
*
* Private procedure
*
*
* */
private fun buildPathName(): String {
val sb = StringBuilder()
val iterator = path.iterator()
while (iterator.hasNext()) {
val s = iterator.next()
if (s.trim { it <= ' ' } == "") continue
sb.append(s)
if (iterator.hasNext()) sb.append(".")
}
return sb.toString()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment