Kotlin - 使用 SimpleXml 解析带有列表的 Xml 响应
Kotlin - parsing Xml response with list using SimpleXml
我正在尝试解析来自 API 的 XML 响应,但 "Field" 元素列表有问题。我正在尝试使用元素内的属性和文本创建 Field 对象,例如此对象的属性,但找不到我需要为元素文本使用的注释。
我尝试使用
@get:Text
@set:Text
和
@field:Text
但得到同样的错误
java.lang.RuntimeException: org.simpleframework.xml.core.PersistenceException: Constructor not matched for class NetworkTestContainer$Field
任何人都可以为这种情况建议一个合适的注释吗?
有XML个结构:
<response>
<commands>
<command>
<nick>QUEUECAUSES_LIST</nick>
<result>
<DATASET Version="1.0" Class="TQueryAdv" Name="">
<Row Index="1">
<Field Name="SHOPID" Type="6" Size="0">-1</Field>
<Field Name="IDCODE" Type="6" Size="0">3000000000001</Field>
<Field Name="CAPTION" Type="1" Size="255">Консультация</Field>
<Field Name="PRIORITY" Type="6" Size="0">0</Field>
</Row>
<Row Index="2">
<Field Name="SHOPID" Type="6" Size="0">-1</Field>
<Field Name="IDCODE" Type="6" Size="0">3000000000021</Field>
<Field Name="CAPTION" Type="1" Size="255">Очередь</Field>
<Field Name="PRIORITY" Type="6" Size="0">1</Field>
</Row>
</DATASET>
</result>
</command>
</commands>
</response>
这是模特
@Root(name = "response", strict = false)
data class NetworkTestContainer(
@field:ElementList(name = "commands")
val commands : List<Command>
) {
@Root(name = "command", strict = false)
data class Command(
@field:Element
val nick : String,
@field:Element(name = "result")
val result : Result
)
@Root(name = "result", strict = false)
data class Result(
@field:Element(name = "DATASET")
val dataSet: DataSet
)
@Root(name = "DATASET", strict = false)
data class DataSet(
@field:Attribute(name = "Version")
val version : String,
@field:Attribute(name = "Class")
val className : String,
@field:Attribute(name = "Name")
val Name : String,
@field:ElementList(inline = true)
val rows : List<Row>
)
@Root(name = "Row", strict = false)
data class Row(
@field:Attribute(name = "Index")
val index : Int,
@field:ElementList(inline = true, entry = "Field")
val fields: List<Field>
// @field:ElementMap(attribute = true, entry = "Field", key = "Name", inline = true)
// val fields : Map<String, String>
)
@Root(name = "Field", strict = false)
data class Field (
@field:Attribute(name = "Name", required = false)
val key : String,
@field:Attribute(name = "Type", required = false)
val type : Int,
@field:Attribute(name = "Size", required = false)
val size : Int,
@get:Text
@set:Text
var text : String
)
}
我不熟悉这个库,但我尝试调试它,这是我的发现。
根据 the documentation,该库通过使用带 field/getter/setter 注释的空构造函数或使用构造函数注入 + getter 注释来工作。
在第一种情况下,您需要摆脱 data
修饰符并使用 @field:
注释,加上 lateinit var
或其他方法在创建对象时初始化所有字段。示例:
import org.simpleframework.xml.Attribute
import org.simpleframework.xml.Element
import org.simpleframework.xml.ElementList
import org.simpleframework.xml.Root
import org.simpleframework.xml.core.Persister
@Root(name = "response", strict = false)
class NetworkTestContainer {
@field:ElementList(name = "commands")
lateinit var commands: List<Command>
override fun toString(): String {
return "NetworkTestContainer(commands=$commands)"
}
}
@Root(name = "command", strict = false)
class Command {
@field:Element(name = "nick")
lateinit var nick: String
@field:Element(name = "result")
lateinit var result: Result
override fun toString(): String {
return "Command(nick='$nick', result=$result)"
}
}
@Root(name = "result", strict = false)
class Result {
@field:Element(name = "DATASET")
lateinit var dataSet: DataSet
override fun toString(): String {
return "Result(dataSet=$dataSet)"
}
}
@Root(name = "DATASET", strict = false)
class DataSet {
@field:Attribute(name = "Version")
lateinit var version: String
@field:Attribute(name = "Class")
lateinit var className: String
@field:Attribute(name = "Name")
lateinit var name: String
@field:ElementList(inline = true)
lateinit var rows: List<Row>
override fun toString(): String {
return "DataSet(version='$version', className='$className', name='$name', rows=$rows)"
}
}
@Root(name = "Row", strict = false)
class Row {
@field:Attribute(name = "Index")
var index: Int = 0
@field:ElementList(inline = true, entry = "Field")
lateinit var fields: List<Field>
override fun toString(): String {
return "Row(index=$index, fields=$fields)"
}
// @field:ElementMap(attribute = true, entry = "Field", key = "Name", inline = true)
// val fields : Map<String, String>
}
@Root(name = "Field", strict = false)
class Field {
@field:Attribute(name = "Name", required = false)
lateinit var key: String
@field:Attribute(name = "Type", required = false)
var type: Int = 0
@field:Attribute(name = "Size", required = false)
var size: Int = 0
override fun toString(): String {
return "Field(key='$key', type=$type, size=$size)"
}
}
object Main {
@JvmStatic
fun main(args: Array<String>) {
val deserializer = Persister()
val file = Main::class.java.getResourceAsStream("input.xml")
val container = deserializer.read(NetworkTestContainer::class.java, file)
println(container)
}
}
这会打印:
NetworkTestContainer(commands=[Command(nick='QUEUECAUSES_LIST', result=Result(dataSet=DataSet(version='1.0', className='TQueryAdv', name='', rows=[Row(index=1, fields=[Field(key='SHOPID', type=6, size=0), Field(key='IDCODE', type=6, size=0), Field(key='CAPTION', type=1, size=255), Field(key='PRIORITY', type=6, size=0)]), Row(index=2, fields=[Field(key='SHOPID', type=6, size=0), Field(key='IDCODE', type=6, size=0), Field(key='CAPTION', type=1, size=255), Field(key='PRIORITY', type=6, size=0)])])))])
如您所见,这并不理想,但它确实有效。
一种 - 可能 - 更好的方法是使用构造函数注入,这也需要带注释的 getter 或字段(如上面链接的文档中指定)。
为此,您需要使用 @param:
注释(因为您需要注释构造函数参数)加上 @get:
注释(因为您需要注释 getter)。示例:
import org.simpleframework.xml.Attribute
import org.simpleframework.xml.Element
import org.simpleframework.xml.ElementList
import org.simpleframework.xml.Root
import org.simpleframework.xml.Text
import org.simpleframework.xml.core.Persister
@Root(name = "response", strict = false)
data class NetworkTestContainer(
@param:ElementList(name = "commands")
@get:ElementList(name = "commands")
val commands: List<Command>
) {
@Root(name = "command", strict = false)
data class Command(
@param:Element(name="nick")
@get:Element(name="nick")
val nick: String,
@param:Element(name = "result")
@get:Element(name = "result")
val result: Result
)
@Root(name = "result", strict = false)
data class Result(
@param:Element(name = "DATASET")
@get:Element(name = "DATASET")
val dataSet: DataSet
)
@Root(name = "DATASET", strict = false)
data class DataSet(
@param:Attribute(name = "Version")
@get:Attribute(name = "Version")
val version: String,
@param:Attribute(name = "Class")
@get:Attribute(name = "Class")
val className: String,
@param:Attribute(name = "Name")
@get:Attribute(name = "Name")
val Name: String,
@param:ElementList(inline = true)
@get:ElementList(inline = true)
val rows: List<Row>
)
@Root(name = "Row", strict = false)
data class Row(
@param:Attribute(name = "Index")
@get:Attribute(name = "Index")
val index: Int,
@param:ElementList(inline = true, entry = "Field")
@get:ElementList(inline = true, entry = "Field")
val fields: List<Field>
// @field:ElementMap(attribute = true, entry = "Field", key = "Name", inline = true)
// val fields : Map<String, String>
)
@Root(name = "Field", strict = false)
data class Field(
@param:Attribute(name = "Name", required = false)
@get:Attribute(name = "Name", required = false)
val key: String,
@param:Attribute(name = "Type", required = false)
@get:Attribute(name = "Type", required = false)
val type: Int,
@param:Attribute(name = "Size", required = false)
@get:Attribute(name = "Size", required = false)
val size: Int
)
}
object Main {
@JvmStatic
fun main(args: Array<String>) {
val deserializer = Persister()
val file = Main::class.java.getResourceAsStream("input.xml")
val container = deserializer.read(NetworkTestContainer::class.java, file)
println(container)
}
}
这会打印与上面相同的内容(除了字符串的一些样式差异之外):
NetworkTestContainer(commands=[Command(nick=QUEUECAUSES_LIST, result=Result(dataSet=DataSet(version=1.0, className=TQueryAdv, Name=, rows=[Row(index=1, fields=[Field(key=SHOPID, type=6, size=0), Field(key=IDCODE, type=6, size=0), Field(key=CAPTION, type=1, size=255), Field(key=PRIORITY, type=6, size=0)]), Row(index=2, fields=[Field(key=SHOPID, type=6, size=0), Field(key=IDCODE, type=6, size=0), Field(key=CAPTION, type=1, size=255), Field(key=PRIORITY, type=6, size=0)])])))])
我正在尝试解析来自 API 的 XML 响应,但 "Field" 元素列表有问题。我正在尝试使用元素内的属性和文本创建 Field 对象,例如此对象的属性,但找不到我需要为元素文本使用的注释。 我尝试使用
@get:Text
@set:Text
和
@field:Text
但得到同样的错误
java.lang.RuntimeException: org.simpleframework.xml.core.PersistenceException: Constructor not matched for class NetworkTestContainer$Field
任何人都可以为这种情况建议一个合适的注释吗?
有XML个结构:
<response>
<commands>
<command>
<nick>QUEUECAUSES_LIST</nick>
<result>
<DATASET Version="1.0" Class="TQueryAdv" Name="">
<Row Index="1">
<Field Name="SHOPID" Type="6" Size="0">-1</Field>
<Field Name="IDCODE" Type="6" Size="0">3000000000001</Field>
<Field Name="CAPTION" Type="1" Size="255">Консультация</Field>
<Field Name="PRIORITY" Type="6" Size="0">0</Field>
</Row>
<Row Index="2">
<Field Name="SHOPID" Type="6" Size="0">-1</Field>
<Field Name="IDCODE" Type="6" Size="0">3000000000021</Field>
<Field Name="CAPTION" Type="1" Size="255">Очередь</Field>
<Field Name="PRIORITY" Type="6" Size="0">1</Field>
</Row>
</DATASET>
</result>
</command>
</commands>
</response>
这是模特
@Root(name = "response", strict = false)
data class NetworkTestContainer(
@field:ElementList(name = "commands")
val commands : List<Command>
) {
@Root(name = "command", strict = false)
data class Command(
@field:Element
val nick : String,
@field:Element(name = "result")
val result : Result
)
@Root(name = "result", strict = false)
data class Result(
@field:Element(name = "DATASET")
val dataSet: DataSet
)
@Root(name = "DATASET", strict = false)
data class DataSet(
@field:Attribute(name = "Version")
val version : String,
@field:Attribute(name = "Class")
val className : String,
@field:Attribute(name = "Name")
val Name : String,
@field:ElementList(inline = true)
val rows : List<Row>
)
@Root(name = "Row", strict = false)
data class Row(
@field:Attribute(name = "Index")
val index : Int,
@field:ElementList(inline = true, entry = "Field")
val fields: List<Field>
// @field:ElementMap(attribute = true, entry = "Field", key = "Name", inline = true)
// val fields : Map<String, String>
)
@Root(name = "Field", strict = false)
data class Field (
@field:Attribute(name = "Name", required = false)
val key : String,
@field:Attribute(name = "Type", required = false)
val type : Int,
@field:Attribute(name = "Size", required = false)
val size : Int,
@get:Text
@set:Text
var text : String
)
}
我不熟悉这个库,但我尝试调试它,这是我的发现。
根据 the documentation,该库通过使用带 field/getter/setter 注释的空构造函数或使用构造函数注入 + getter 注释来工作。
在第一种情况下,您需要摆脱 data
修饰符并使用 @field:
注释,加上 lateinit var
或其他方法在创建对象时初始化所有字段。示例:
import org.simpleframework.xml.Attribute
import org.simpleframework.xml.Element
import org.simpleframework.xml.ElementList
import org.simpleframework.xml.Root
import org.simpleframework.xml.core.Persister
@Root(name = "response", strict = false)
class NetworkTestContainer {
@field:ElementList(name = "commands")
lateinit var commands: List<Command>
override fun toString(): String {
return "NetworkTestContainer(commands=$commands)"
}
}
@Root(name = "command", strict = false)
class Command {
@field:Element(name = "nick")
lateinit var nick: String
@field:Element(name = "result")
lateinit var result: Result
override fun toString(): String {
return "Command(nick='$nick', result=$result)"
}
}
@Root(name = "result", strict = false)
class Result {
@field:Element(name = "DATASET")
lateinit var dataSet: DataSet
override fun toString(): String {
return "Result(dataSet=$dataSet)"
}
}
@Root(name = "DATASET", strict = false)
class DataSet {
@field:Attribute(name = "Version")
lateinit var version: String
@field:Attribute(name = "Class")
lateinit var className: String
@field:Attribute(name = "Name")
lateinit var name: String
@field:ElementList(inline = true)
lateinit var rows: List<Row>
override fun toString(): String {
return "DataSet(version='$version', className='$className', name='$name', rows=$rows)"
}
}
@Root(name = "Row", strict = false)
class Row {
@field:Attribute(name = "Index")
var index: Int = 0
@field:ElementList(inline = true, entry = "Field")
lateinit var fields: List<Field>
override fun toString(): String {
return "Row(index=$index, fields=$fields)"
}
// @field:ElementMap(attribute = true, entry = "Field", key = "Name", inline = true)
// val fields : Map<String, String>
}
@Root(name = "Field", strict = false)
class Field {
@field:Attribute(name = "Name", required = false)
lateinit var key: String
@field:Attribute(name = "Type", required = false)
var type: Int = 0
@field:Attribute(name = "Size", required = false)
var size: Int = 0
override fun toString(): String {
return "Field(key='$key', type=$type, size=$size)"
}
}
object Main {
@JvmStatic
fun main(args: Array<String>) {
val deserializer = Persister()
val file = Main::class.java.getResourceAsStream("input.xml")
val container = deserializer.read(NetworkTestContainer::class.java, file)
println(container)
}
}
这会打印:
NetworkTestContainer(commands=[Command(nick='QUEUECAUSES_LIST', result=Result(dataSet=DataSet(version='1.0', className='TQueryAdv', name='', rows=[Row(index=1, fields=[Field(key='SHOPID', type=6, size=0), Field(key='IDCODE', type=6, size=0), Field(key='CAPTION', type=1, size=255), Field(key='PRIORITY', type=6, size=0)]), Row(index=2, fields=[Field(key='SHOPID', type=6, size=0), Field(key='IDCODE', type=6, size=0), Field(key='CAPTION', type=1, size=255), Field(key='PRIORITY', type=6, size=0)])])))])
如您所见,这并不理想,但它确实有效。
一种 - 可能 - 更好的方法是使用构造函数注入,这也需要带注释的 getter 或字段(如上面链接的文档中指定)。
为此,您需要使用 @param:
注释(因为您需要注释构造函数参数)加上 @get:
注释(因为您需要注释 getter)。示例:
import org.simpleframework.xml.Attribute
import org.simpleframework.xml.Element
import org.simpleframework.xml.ElementList
import org.simpleframework.xml.Root
import org.simpleframework.xml.Text
import org.simpleframework.xml.core.Persister
@Root(name = "response", strict = false)
data class NetworkTestContainer(
@param:ElementList(name = "commands")
@get:ElementList(name = "commands")
val commands: List<Command>
) {
@Root(name = "command", strict = false)
data class Command(
@param:Element(name="nick")
@get:Element(name="nick")
val nick: String,
@param:Element(name = "result")
@get:Element(name = "result")
val result: Result
)
@Root(name = "result", strict = false)
data class Result(
@param:Element(name = "DATASET")
@get:Element(name = "DATASET")
val dataSet: DataSet
)
@Root(name = "DATASET", strict = false)
data class DataSet(
@param:Attribute(name = "Version")
@get:Attribute(name = "Version")
val version: String,
@param:Attribute(name = "Class")
@get:Attribute(name = "Class")
val className: String,
@param:Attribute(name = "Name")
@get:Attribute(name = "Name")
val Name: String,
@param:ElementList(inline = true)
@get:ElementList(inline = true)
val rows: List<Row>
)
@Root(name = "Row", strict = false)
data class Row(
@param:Attribute(name = "Index")
@get:Attribute(name = "Index")
val index: Int,
@param:ElementList(inline = true, entry = "Field")
@get:ElementList(inline = true, entry = "Field")
val fields: List<Field>
// @field:ElementMap(attribute = true, entry = "Field", key = "Name", inline = true)
// val fields : Map<String, String>
)
@Root(name = "Field", strict = false)
data class Field(
@param:Attribute(name = "Name", required = false)
@get:Attribute(name = "Name", required = false)
val key: String,
@param:Attribute(name = "Type", required = false)
@get:Attribute(name = "Type", required = false)
val type: Int,
@param:Attribute(name = "Size", required = false)
@get:Attribute(name = "Size", required = false)
val size: Int
)
}
object Main {
@JvmStatic
fun main(args: Array<String>) {
val deserializer = Persister()
val file = Main::class.java.getResourceAsStream("input.xml")
val container = deserializer.read(NetworkTestContainer::class.java, file)
println(container)
}
}
这会打印与上面相同的内容(除了字符串的一些样式差异之外):
NetworkTestContainer(commands=[Command(nick=QUEUECAUSES_LIST, result=Result(dataSet=DataSet(version=1.0, className=TQueryAdv, Name=, rows=[Row(index=1, fields=[Field(key=SHOPID, type=6, size=0), Field(key=IDCODE, type=6, size=0), Field(key=CAPTION, type=1, size=255), Field(key=PRIORITY, type=6, size=0)]), Row(index=2, fields=[Field(key=SHOPID, type=6, size=0), Field(key=IDCODE, type=6, size=0), Field(key=CAPTION, type=1, size=255), Field(key=PRIORITY, type=6, size=0)])])))])