类似GraphQL,自定义json输出,Kotlin实现

第一次见GraphQL的时候是去年,完全被惊呆了,心想这么牛逼,前台想要什么就可以拿到什么数据,不想要的完全可以不获取,之后就开始尝试使用GraphQL,然后发现入门失败。

但是自己又有这方面的需求,比如有一个数据格式(暂定用户信息),有多个接口需要获取它的信息,而且每个接口的需求不一样,也可能后端为了限制某些字段不给某个接口返回。

例如:用户(id,姓名,年龄,爱好,出生日期,身份证,密码,头像)

A接口:只需要(姓名、年龄、爱好),其他信息无权限获取

B接口:需要(姓名、出生日期、身份证、头像),其他信息并不关心,且密码无法获取

C接口:有权限获取全部信息,但又不需要全部信息

当遇到这种情况的时候基础的json可以较方便的解决,但是如果遇到联级的信息呢??

比如:国家(名称,地区(名称,市(名称,县....)))这样联级的信息,就难搞了。

因此需要GraphQL这样的工具,直接写一个字符串去过滤出需要的数据({country{name,Area{name,city{name,county}}}})只通过这个字符串就过滤出我们需要的json数据。

下面进入代码阶段(完整的代码在最后面贴出来)

开发语言:Kotlin

开发平台:Windows

开发工具:Idea

完整代码:https://gitee.com/houkunlin/goour-utils/blob/master/src/main/kotlin/cn/goour/kotlin/tools/MapTools.kt

第一步:写一个字符串解析方法,解析过滤字符串,解析成map键值对形式,key为字段信息,value为子类嵌套过滤字符串

private fun getKeyMap(key: String): HashMap<String, String?> {
    val keyMap = hashMapOf<String, String?>()
    var lastIndex = 0
    var endIndex = 0
    var left = 0
    key.forEachIndexed { index, c ->
        when (c) {
            ',' -> {
                if (left == 0 && lastIndex < index) {
                    endIndex = index
                    keyMap[key.substring(lastIndex, endIndex).replace(",", "")] = null
                    lastIndex = index + 1
                }
            }
            '{' -> {
                if (left == 0) {
                    endIndex = index
                }
                ++left

            }
            '}' -> {
                if (--left == 0) {
                    keyMap[key.substring(lastIndex, endIndex).replace(",", "")] = key.substring(endIndex + 1, index)
                    lastIndex = index + 2
                }
            }
        }
        if (index == key.lastIndex && lastIndex < index) {
            keyMap[key.substring(lastIndex).replace(",", "")] = null
        }
    }
    return keyMap
}

第二步:就是开始对bean的字段进行过滤了,返回一个map,key为字段信息,value为字段值

fun Any.toMapInclude(key: String): Map<String, Any?> {
    val keyMap = getKeyMap(key)
    val fields = this.javaClass.declaredFields
    val map = hashMapOf<String, Any?>()
    fields.forEach {
        it.isAccessible = true
        if (keyMap.contains(it.name) && it.name != "Companion") {
            val value = keyMap[it.name]
            if (value != null) {
                val obj = it.get(this)
                when {
                    Iterable::class.java.isInstance(obj) -> {
                        val list = arrayListOf<Any>()
                        obj as Collection<*>
                        obj.forEach {
                            if (it != null) {
                                list += it.toMapInclude(value)
                            }
                        }
                        map[it.name] = list
                    }
                    it.type.isArray -> {
                        val list = arrayListOf<Any>()
                        obj as Array<*>
                        obj.forEach {
                            if (it != null) {
                                list += it.toMapInclude(value)
                            }
                        }
                        map[it.name] = list
                    }
                    else -> map[it.name] = obj.toMapInclude(value)
                }
            } else {
                val obj = it.get(this)
                when {
                    it.type.isArray -> {
                        obj as Array<*>
                        map[it.name] = obj.toList()
                    }
                    else -> map[it.name] = it.get(this)
                }
            }
        }
    }
    return map
}

OK,完工,之后开始测试跑一下


fun main(args: Array<String>) {
    val g = Country(name = "中国",
            area = Area(name = "广西", gps = arrayOf("1.x", "2.x"),
                    city = City(name = "桂林",
                            county = listOf(
                                    County(name = "测试县城1", gps = arrayOf("1.x", "2.x")),
                                    County(name = "测试县城2", gps = arrayOf("11.x", "22.x"))
                            )
                    )
            )
    )
    val str = "name,area{name,gps,city{name,county{name}}}"
    println(g.toMapInclude(str))//{area={city={name=桂林, county=[{name=测试县城1}, {name=测试县城2}]}, name=广西, gps=[1.x, 2.x]}, name=中国}
	
    println(g.toMapInclude(str).toJsonString())//{"area":{"city":{"name":"桂林","county":[{"name":"测试县城1"},{"name":"测试县城2"}]},"name":"广西","gps":["1.x","2.x"]},"name":"中国"}
}

下面贴另一份测试结果

fun main(args: Array<String>) {
    val g = Country(name = "中国",
            area = Area(name = "广西", gps = arrayOf("1.x", "2.x"),
                    city = City(name = "桂林",
                            county = listOf(
                                    County(name = "测试县城1", gps = arrayOf("1.x", "2.x")),
                                    County(name = "测试县城2", gps = arrayOf("11.x", "22.x"))
                            )
                    )
            )
    )
    val str = "name,area{name,gps,city{name,county{name}}}"
    println(g.toMapInclude(str))
    println(g.toMapInclude(str).toJsonString())


    var start = System.currentTimeMillis()
    for (i in 1..100000) {
        g.toMap()
    }
    var end = System.currentTimeMillis()
    println("转map耗时:${(end - start) / 1000.0}s")

    start = System.currentTimeMillis()
    for (i in 1..100000) {
        g.toMapInclude(str)
    }
    end = System.currentTimeMillis()
    println("过滤耗时:${(end - start) / 1000.0}s")

    start = System.currentTimeMillis()
    for (i in 1..100000) {
        JSONObject.toJSON(g)
    }
    end = System.currentTimeMillis()
    println("json耗时:${(end - start) / 1000.0}s")

    start = System.currentTimeMillis()
    for (i in 1..100000) {
        g.toMapInclude(str).toJsonString()
    }
    end = System.currentTimeMillis()
    println("转map转json耗时:${(end - start) / 1000.0}s")
}
/*
运行结果
电脑处理器:AMD A12-9700P 2.50GHz
内存RAM:8.00GB(7.46GB可用)
{area={city={name=桂林, county=[{name=测试县城1}, {name=测试县城2}]}, name=广西, gps=[1.x, 2.x]}, name=中国}
{"area":{"city":{"name":"桂林","county":[{"name":"测试县城1"},{"name":"测试县城2"}]},"name":"广西","gps":["1.x","2.x"]},"name":"中国"}
转map耗时:0.097s
过滤耗时:1.213s
json耗时:1.092s
转map转json耗时:2.074s
*/

下面贴全部完整的代码

package cn.goour.kotlin.tools

import com.alibaba.fastjson.JSONObject

fun Any.toMap(): Map<String, Any?> {
    val fields = this.javaClass.declaredFields
    val map = hashMapOf<String, Any?>()
    fields.forEach {
        it.isAccessible = true
        if (it.name != "Companion") {
            map[it.name] = it.get(this)
        }
//        println(it.name + " " + it.type.name + " " + it.get(this))
    }
    return map
}

private fun getKeyMap(key: String): HashMap<String, String?> {
//    println(key)
    val keyMap = hashMapOf<String, String?>()
    var lastIndex = 0
    var endIndex = 0
    var left = 0
    key.forEachIndexed { index, c ->
        //        println("$index = $c")
        when (c) {
            ',' -> {
                if (left == 0 && lastIndex < index) {
                    endIndex = index
//                    println("$lastIndex -- $endIndex")
//                    println("${key.substring(lastIndex, endIndex)} -- null")
                    keyMap[key.substring(lastIndex, endIndex).replace(",", "")] = null
                    lastIndex = index + 1
                }
            }
            '{' -> {
                if (left == 0) {
                    endIndex = index
                }
                ++left

            }
            '}' -> {
                if (--left == 0) {
//                    println("$lastIndex -- $endIndex -- $index")
//                    println("${key.substring(lastIndex, endIndex)} -- ${key.substring(endIndex + 1, index)}")
                    keyMap[key.substring(lastIndex, endIndex).replace(",", "")] = key.substring(endIndex + 1, index)
                    lastIndex = index + 2
                }
            }
        }
        if (index == key.lastIndex && lastIndex < index) {
//            println("$lastIndex -- $endIndex")
//            println("${key.substring(lastIndex)} -- null")
            keyMap[key.substring(lastIndex).replace(",", "")] = null
        }
    }
    return keyMap
}

fun Any.toMapInclude(key: String): Map<String, Any?> {
//    println(key)
    val keyMap = getKeyMap(key)
    val fields = this.javaClass.declaredFields
    val map = hashMapOf<String, Any?>()
    fields.forEach {
        it.isAccessible = true
        if (keyMap.contains(it.name) && it.name != "Companion") {
            val value = keyMap[it.name]
            if (value != null) {
                val obj = it.get(this)
                when {
                    Iterable::class.java.isInstance(obj) -> {
                        val list = arrayListOf<Any>()
                        obj as Collection<*>
                        obj.forEach {
                            if (it != null) {
                                list += it.toMapInclude(value)
                            }
                        }
                        map[it.name] = list
                    }
                    it.type.isArray -> {
                        val list = arrayListOf<Any>()
                        obj as Array<*>
                        obj.forEach {
                            if (it != null) {
                                list += it.toMapInclude(value)
                            }
                        }
                        map[it.name] = list
                    }
                    else -> map[it.name] = obj.toMapInclude(value)
                }
            } else {
                val obj = it.get(this)
                when {
                    it.type.isArray -> {
                        obj as Array<*>
                        map[it.name] = obj.toList()
                    }
                    else -> map[it.name] = it.get(this)
                }
            }
        }
    }
    return map
}


fun Map<*, *>.toJsonString(dateFormat: String = "yyyy-MM-dd HH:mm:ss"): String {
    return JSONObject.toJSONStringWithDateFormat(this, dateFormat)
}

fun main(args: Array<String>) {
    val g = Country(name = "中国",
            area = Area(name = "广西", gps = arrayOf("1.x", "2.x"),
                    city = City(name = "桂林",
                            county = listOf(
                                    County(name = "测试县城1", gps = arrayOf("1.x", "2.x")),
                                    County(name = "测试县城2", gps = arrayOf("11.x", "22.x"))
                            )
                    )
            )
    )
    val str = "name,area{name,gps,city{name,county{name}}}"
    println(g.toMapInclude(str))
    println(g.toMapInclude(str).toJsonString())


    var start = System.currentTimeMillis()
    for (i in 1..100000) {
        g.toMap()
    }
    var end = System.currentTimeMillis()
    println("转map耗时:${(end - start) / 1000.0}s")

    start = System.currentTimeMillis()
    for (i in 1..100000) {
        g.toMapInclude(str)
    }
    end = System.currentTimeMillis()
    println("过滤耗时:${(end - start) / 1000.0}s")

    start = System.currentTimeMillis()
    for (i in 1..100000) {
        JSONObject.toJSON(g)
    }
    end = System.currentTimeMillis()
    println("json耗时:${(end - start) / 1000.0}s")

    start = System.currentTimeMillis()
    for (i in 1..100000) {
        g.toMapInclude(str).toJsonString()
    }
    end = System.currentTimeMillis()
    println("转map转json耗时:${(end - start) / 1000.0}s")
}

data class Country(
        var name: String,
        var area: Area? = null
)

data class Area(
        var name: String,
        var gps: Array<String>? = null,
        var city: City? = null
)

data class City(
        var name: String,
        var county: List<County>
)

data class County(
        var name: String,
        var gps: Array<String>? = null
)

 

转载于:https://my.oschina.net/houkunlin/blog/1618145

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值