gpt4 book ai didi

xml - Kotlin 上 JacksonXmlElementWrapper 的问题

转载 作者:行者123 更新时间:2023-12-04 17:30:04 26 4
gpt4 key购买 nike

我正在尝试在 Android Studio 上使用 Jackson 和 Kotlin 解析 XML 文件。特别是,我想在 jackson-module-kotlin 中运行以下测试用例的代码:

https://github.com/FasterXML/jackson-module-kotlin/blob/master/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/github/Github153.kt

    @JacksonXmlRootElement(localName = "MyPojo")
data class MyDataPojo (
@JacksonXmlElementWrapper(localName = "elements")
@JacksonXmlProperty(localName = "element")
val elements : List<MyDataElement>
)

data class MyDataElement (
@JacksonXmlProperty(localName = "value", isAttribute = true)
var value: String
)

我正在尝试像这样解析 XML 文件:

    val xmlMapper = XmlMapper().apply {
registerModule(KotlinModule())
}
val pojo = context.resources.assets.open(name).use { input ->
xmlMapper.readValue<MyDataPojo>(input)
}

失败并出现以下异常:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid definition for property `elements` (of type `eu.tilk.wihajster.MyDataPojo`): Could not find creator property with name 'elements' (known Creator properties: [element])

我正在使用 Jackson 2.10.3,这是我在 build.gradle 中的依赖项部分:

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.fasterxml.jackson.core:jackson-core:2.10.3'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.10.3'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.10.3'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.10.3'
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.10.3"
implementation 'javax.xml.stream:stax-api:1.0-2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

我做错了什么?此代码是从 jackson-module-kotlin 的测试中复制的,因此我相信它应该可以正常工作。

最佳答案

这是一个 open issue使用 Jackson Kotlin 模块:

apatrida commented on 14 Jul 2018
JacksonXmlElementWrapper cannot be on a parameter therefore no property name of elements gets added to the creator parameter, and instead JacksonXmlProperty ends up on the parameter which is the wrong name. So then the mapper tries to pass in elements but the parameter appears to be property element

您链接的测试实际上预计会因数据类而失败:

@JacksonXmlRootElement(localName = "MyPojo")
data class MyDataPojo (
@JacksonXmlElementWrapper(localName = "elements")
@JacksonXmlProperty(localName = "element")
val elements : List<MyDataElement>
)

@Test
// Conflict between the annotations that is not current resolvable.
fun test_data_class() {
expectFailure<InvalidDefinitionException>("Problem with conflicting annotations related to #153 has been fixed!") {
// I create a pojo from the xml using the data classes
val pojoFromXml = mapper.readValue(xml, MyDataPojo::class.java)

// I create a xml from the pojo
val xmlFromPojo = mapper.writeValueAsString(pojoFromXml)

// I compare the original xml with the xml generated from the pojo
assertEquals(xml, xmlFromPojo)
}
}

然而,它预计适用于数据类:

@JacksonXmlRootElement(localName = "MyPojo")
class MyPojo {
@JacksonXmlElementWrapper(localName = "elements")
@JacksonXmlProperty(localName = "element")
var list: List<MyElement>? = null
}

@Test
fun test_class() {
// I create a pojo from the xml using the standard classes
val pojoFromXml = mapper.readValue(xml, MyPojo::class.java)

// I create a xml from the pojo
val xmlFromPojo = mapper.writeValueAsString(pojoFromXml)

// I compare the original xml with the xml generated from the pojo
assertEquals(xml, xmlFromPojo)
}

如您所见——尽管 Kotlin 让它变得有点困难——不同之处在于 elements 在数据类中是一个构造函数参数,但在常规类中是一个字段。

在不创建自定义(反)序列化程序的情况下,我能想到的最佳解决方法是

  • 将主构造函数设为私有(private)
  • 让字段成为带有私有(private) setter 的 lateinit var
  • 手动覆盖 hashCode()equals()toString()

例如:

@JacksonXmlRootElement(localName = "MyPojo")
class WorkaroundPojo private constructor() {
constructor(list: List<MyElement>) : this() {
this.list = list
}

@JacksonXmlElementWrapper(localName = "elements")
@JacksonXmlProperty(localName = "element")
lateinit var list: List<MyElement>
private set

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as WorkaroundPojo

if (list != other.list) return false

return true
}

override fun hashCode(): Int {
return list.hashCode()
}

override fun toString(): String {
return "WorkaroundPojo(list=$list)"
}
}

@Test
fun test_workaround() {
val pojoFromXml = mapper.readValue(xml, WorkaroundPojo::class.java)
val xmlFromPojo = mapper.writeValueAsString(pojoFromXml)
assertEquals(xml, xmlFromPojo)
}

这是一个 JUnit5 测试,其中包含一个更复杂的示例,展示了如何使用 Jackson 2.13.0 将上述内容与继承、枚举等结合起来:

import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.dataformat.xml.XmlMapper
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule
import com.fasterxml.jackson.module.kotlin.kotlinModule
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import javax.xml.bind.annotation.XmlEnumValue

class StackOverflow {

abstract class ActivatableElement {
abstract val active: Boolean
}

class ComplexExample private constructor(
override val active: Boolean = false,
val myEnum: MyEnum = MyEnum.First,
) : ActivatableElement() {

constructor(active: Boolean, myEnum: MyEnum, myElements: List<MyElement>) : this(active, myEnum) {
this.elements = myElements
}

@JacksonXmlElementWrapper(localName = "elementList")
@JacksonXmlProperty(localName = "element")
lateinit var elements: List<MyElement>
private set

data class MyElement(
val name: String,
val number: Int
)

enum class MyEnum {
@XmlEnumValue("First value of MyEnum")
First,

@XmlEnumValue("Second value of MyEnum")
Second,
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as ComplexExample

if (active != other.active) return false
if (myEnum != other.myEnum) return false
if (elements != other.elements) return false

return true
}

override fun hashCode(): Int {
var result = active.hashCode()
result = 31 * result + myEnum.hashCode()
result = 31 * result + elements.hashCode()
return result
}
}

val xml = """
<ComplexExample>
<active>true</active>
<myEnum>Second value of MyEnum</myEnum>
<elementList>
<element>
<name>First Item</name>
<number>1</number>
</element>
<element>
<name>Second Item</name>
<number>2</number>
</element>
<element>
<name>Third Item</name>
<number>3</number>
</element>
</elementList>
</ComplexExample>
""".trimIndent()

@Test
fun test_complex_example() {
val mapper = XmlMapper.builder()
.addModule(kotlinModule())
.addModule(JaxbAnnotationModule())
.build()
.enable(SerializationFeature.INDENT_OUTPUT)

val example = mapper.readValue(xml, ComplexExample::class.java)
val xmlFromExample = mapper.writeValueAsString(example).trim()
Assertions.assertEquals(xml, xmlFromExample)

val expected = ComplexExample(
active = true,
myEnum = ComplexExample.MyEnum.Second,
myElements = listOf(
ComplexExample.MyElement("First Item", 1),
ComplexExample.MyElement("Second Item", 2),
ComplexExample.MyElement("Third Item", 3),
)
)
Assertions.assertEquals(expected, example)
Assertions.assertEquals(expected.hashCode(), example.hashCode())
}
}

关于xml - Kotlin 上 JacksonXmlElementWrapper 的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60655621/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com