You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取Kotlin中带@Mask注解成员属性的JsonPath?

获取Kotlin中带@Mask注解属性的JsonPath方案

核心思路

通过Kotlin反射遍历目标类的所有成员属性,递归处理嵌套类、列表、泛型类型:

  • 若属性带有@Mask注解,记录当前拼接的JsonPath
  • 若属性是嵌套类、泛型实例或列表元素类型,继续递归遍历内部属性,拼接路径(列表元素用[*]匹配所有项)

实现代码

首先定义@Mask注解:

annotation class Mask

然后实现查找工具类:

import kotlin.reflect.KClass
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.full.starProjectedType
import kotlin.reflect.full.typeParameters
import kotlin.reflect.full.withNullability

object MaskJsonPathFinder {
    fun findMaskPaths(targetClass: KClass<*>): List<String> {
        val result = mutableListOf<String>()
        traverseClass(targetClass, "$", result)
        return result
    }

    private fun traverseClass(currentClass: KClass<*>, currentPath: String, result: MutableList<String>) {
        currentClass.declaredMemberProperties.forEach { property ->
            // 记录带@Mask注解的属性路径
            property.findAnnotation<Mask>()?.let {
                result.add("$currentPath.${property.name}")
            }

            val propertyType = property.returnType.withNullability(false)
            val propertyClass = propertyType.classifier as? KClass<*> ?: return@forEach

            // 处理列表类型
            if (propertyType.isSubtypeOf(List::class.starProjectedType)) {
                val listGenericType = propertyType.arguments.firstOrNull()?.type?.withNullability(false) ?: return@forEach
                val listElementClass = listGenericType.classifier as? KClass<*> ?: return@forEach
                val listPath = "$currentPath.${property.name}[*]"
                traverseClass(listElementClass, listPath, result)
            }
            // 处理泛型类
            else if (propertyClass.typeParameters.isNotEmpty() && !isBasicType(propertyClass)) {
                val genericType = propertyType.arguments.firstOrNull()?.type?.withNullability(false) ?: return@forEach
                val genericClass = genericType.classifier as? KClass<*> ?: return@forEach
                val genericProperty = propertyClass.declaredMemberProperties.firstOrNull { it.name == "generic" } ?: return@forEach
                val genericPath = "$currentPath.${property.name}.${genericProperty.name}"
                traverseClass(genericClass, genericPath, result)
            }
            // 处理普通嵌套类
            else if (!isBasicType(propertyClass)) {
                val nestedPath = "$currentPath.${property.name}"
                traverseClass(propertyClass, nestedPath, result)
            }
        }
    }

    // 判断是否为基础类型,避免无效递归
    private fun isBasicType(kClass: KClass<*>): Boolean {
        return kClass in setOf(
            String::class, Int::class, Long::class, Float::class, Double::class,
            Boolean::class, Byte::class, Short::class, Char::class
        ) || kClass.java.isPrimitive
    }
}

测试代码

用你提供的示例类进行测试:

fun main() {
    val maskPaths = MaskJsonPathFinder.findMaskPaths(Test::class)
    maskPaths.forEach { println(it) }
}

// 示例数据类
class Test(
    @Mask val testField: String?,
    val nested: NestedTest,
    val nestedList: List<NestedTest>?,
    val generic: GenericTest<NestedGenericTest>
)

class NestedTest(
    @Mask val test2: String = "",
    val nestedNestedTest: NestedNestedTest = NestedNestedTest()
)

class NestedNestedTest(
    @Mask
    val test3: String = ""
)

class GenericTest<T>(
    val generic: T
)

class NestedGenericTest(
    @Mask
    val nestedGeneric: String
)

运行结果

执行后输出所有带@Mask注解属性的JsonPath:

$.testField
$.nested.test2
$.nested.nestedNestedTest.test3
$.nestedList[*].test2
$.nestedList[*].nestedNestedTest.test3
$.generic.generic.nestedGeneric

注意事项

  • 如果泛型类的核心属性名不是generic,需要调整工具类中genericProperty的查找逻辑
  • 若需支持Set、Map等其他集合类型,可扩展列表类型的判断分支
  • 基础类型集合可根据业务需求补充更多类型

内容的提问来源于stack exchange,提问作者HongJeongHyeon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 20:55:33