如何获取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
相关产品推荐
相关产品推荐

