Kotlin自定义YAML解析器@YamlArg注解映射失效求助
问题分析
你遇到的核心问题是:Kotlin中构造函数参数(即使标注了@YamlArg)和类的memberProperties并非直接一一对应——如果构造参数没有用val/var声明为类成员属性,memberProperties里根本找不到它;就算用了val/var,直接通过memberProperties获取注解,也可能拿不到标注在构造参数上的@YamlArg(注解目标范围没覆盖的话)。
解决方案
1. 修正@YamlArg注解的目标范围
确保注解能同时作用于构造函数参数和类属性,这样不管注解标在哪,都能被反射获取到:
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.PROPERTY) @Retention(AnnotationRetention.RUNTIME) annotation class YamlArg(val value: String)
2. 重构解析器的映射逻辑:从构造函数参数入手
不要直接遍历memberProperties,而是先处理类的主构造函数参数,建立YAML键到构造参数/类属性的映射:
abstract class AbstractYamlParser { protected fun <T : Any> buildPropertyMap(clazz: KClass<T>): Map<String, KParameter> { val propertyMap = mutableMapOf<String, KParameter>() val primaryConstructor = clazz.primaryConstructor ?: error("No primary constructor found") primaryConstructor.parameters.forEach { param -> // 获取参数上的@YamlArg注解 val yamlArg = param.findAnnotation<YamlArg>() val yamlKey = yamlArg?.value ?: param.name ?: error("Parameter has no name") propertyMap[yamlKey] = param } return propertyMap } // 解析时使用映射表,根据YAML键找到对应的构造参数注入值 fun <T : Any> parse(yamlContent: String, clazz: KClass<T>): T { val yamlMap = parseYamlToMap(yamlContent) // 假设你已实现YAML转Map的逻辑 val propertyMap = buildPropertyMap(clazz) val primaryConstructor = clazz.primaryConstructor!! val args = primaryConstructor.parameters.associateWith { param -> val yamlKey = propertyMap.entries.first { it.value == param }.key yamlMap[yamlKey] ?: error("Missing value for key: $yamlKey") } return primaryConstructor.callBy(args) } // 抽象方法:实现具体的YAML转Map逻辑 abstract fun parseYamlToMap(yamlContent: String): Map<String, Any> }
3. 领域类的正确写法
确保构造参数用val/var声明(需要成为类属性时),保留参数名(Kotlin默认保留,混淆时需配置规则):
data class Person( val name: String, @YamlArg("city of birth") val from: String )
4. 测试用例验证
调用解析器方法,验证映射是否生效:
val yaml = """ name: Alice city of birth: New York """.trimIndent() val parser = object : AbstractYamlParser() { override fun parseYamlToMap(yamlContent: String): Map<String, Any> { // 这里可以用SnakeYAML等库实现YAML转Map,示例简化处理 return mapOf( "name" to "Alice", "city of birth" to "New York" ) } } val person = parser.parse(yaml, Person::class) println(person.from) // 输出: New York
关键注意点
- 若领域类没有主构造函数,需额外处理次构造函数的映射逻辑
- 混淆打包时,要保留类、构造函数参数、注解的反射信息,否则反射会失效
- 若需要支持后期初始化的类属性,可以在
buildPropertyMap中额外遍历memberProperties补充映射关系
内容的提问来源于stack exchange,提问作者nuno
相关产品推荐
相关产品推荐

