
项目介绍:从零开始的Minecraft - Nbt序列化库开发
上一篇:从零开始的Minecraft - Nbt 序列化库开发:ArrayTag实现 & JVM TypeToken的一些碎碎念
摘要:新的需求出现了!nbt数据结构可不止那些简单的primitive类型和array类型,我们需要一个能处理嵌套nbt类型的功能,但现在顶层的接口声明太弱,并不能满足我们的需求,为此我们需要添加新的接口。本文主要内容便是介绍新接口的设计,以及简单聊聊开发新接口时碰见的各种弯路/坑。
前置知识:java/kotlin的基本了解,泛型的了解,jvm反射的了解,本项目基本上层接口设计的了解
术语解释:嵌套类型nbt:支持嵌套其他类型nbt数据的nbt类型,例如mojang标准的 List Tag, Compound Tag
“细节应该依赖于抽象,而非抽象依赖于细节”——最小且完整的接口设计原则
在完成较为简单的nbt数据结构的序列化/反序列化功能后,我们要开始考虑如何序列化嵌套类型的nbt数据结构了。wiki中涉及嵌套类型的数据结构只有两种:List Tag和Compound Tag。二者具体的定义直接参考wiki即可,所以先不详细讲述。我只概述下两种嵌套类型的二进制nbt结构有的共性:
都带有一个Tag head(id+名字)
嵌套其中的对象类型可以是任意nbt类型(包括自己类型)
嵌套的子元素不一定是完整Tag格式(即不包括Tag head),可以只包括Tag head以外的剩余nbt类型(我们将不包括id和tag名字,剩余的内容统称为TagValue)
不支持循环引用(子元素嵌套父元素)
按照格式标准,嵌套最大深度不超过512
(第三条特性来源于List Tag的数据格式,即nbt list中的元素没有Tag head,没有id和名字,id被定义在List Tag中 TagValue部分的开头,详见wiki上List Tag的定义)
这五条需求仍然比较抽象,我们用另一种方式描述下这五条需求下的应用场景会是怎样的:对于Tag-Serdes端,比如一个ListTag,他的子元素可能是任意的Tag类型,这些子元素可能嵌套了其他Tag也可能不嵌套,这些子元素不包含tag head,可能有不固定的数据长度等等。CompoundTag,相当于一个Tag集合,任意的子元素Tag,每个子Tag同样不固定长度。而且这些Tag不支持循环引用,限制结构深度,以后可能还有不知道的新的Tag类型作为其子元素需要处理……
同样的,对于Converter-Tag端,我们可能希望一个将一个List对象转成ListTag中间java对象,同样的List对象拥有类似于ListTag的那些复杂度,也可能有引用循环、超过限制的嵌套深度、子类型可以是任意(以前、现在、未来出现的)java类型,而我们要将其转成一个ListTag中间类型,或者将这个中间类型转回去……
可以说,这未免太复杂了!我们先一步步分析需求:比如说对于ListTag“子元素可以为任意Tag类型,包括已有的和未来可能添加的类型”,换句话说,处理这类嵌套类型的Converter和Serdes不应该知道这些Tag嵌套的子类型是什么样的。那么,我们的基本思路便是:代理模式——Converter和Serdes只处理一种嵌套类型对象,至于他们的子元素是什么类型,不是这些Converter/Serdes需要管的,但他还是得承担起Converter/Serdes的任务,既然他们自己不知道要怎么处理这些子元素,那么就应该交给知道怎么处理的Converter/Serdes来。
然后是“子元素可能有tag head也可能没有”这一问题,那么,这相当于我们需要给serdes的接口添加新的声明,让调用者可以指定序列化/反序列化时是否包含tag head。被代理Serdes也要根据这一需求声明进行对应才做。
然后是循环引用和嵌套深度问题,同样需要在接口上提供一种声明,使得可以记录序列化/反序列化过程中“嵌套了多少层”,比如一个栈记录序列化过程中访问过的所有Tag对象。
为了满足以上,我不得不对上层Tag,Serdes和TagConverter抽象进行改动。在尝试多次接口改动后(以及伴随着的、牵一发动全身的无数相关代码改动),以下是新的上层接口设计:
Tag:
abstract class Tag<NbtRelatedType:Any> {
val name:String?
val value:NbtRelatedType
} 基本没有改动,除了name的类型声明改为(String?),意思为可为空(null),此改动为了符合第三条需求,嵌套的子元素Tag不一定有Tag Head
Serdes:
interface Serdes<NbtRelatedType:Any, I:SerdesCallerIntent> {
fun defaultIntent():I
fun serialize(tag:Tag<NbtRelatedType>, intent:I = defaultIntent()):ByteArray
fun deserialize(data:ByteArray, start:Int, intent:I = defaultIntent()):Pair<Tag<NbtRelatedType>, Int>
val id:Byte
val valueTypeToken:TypeToken<NbtRelatedType>
} 可以说改动相当多,首先添加了一个新的泛型参数I:SerdesIntent,其次还有defaultIntent()函数,以及serialize和deserialize都额外接受一个intent参数,并指定其默认值为defaultIntent()
SerdesCallerIntent可以认为是 使用调用Serdes的一方 提供给Serdes 的一些额外参数,可以形象化地理解为“意图”。也就是说,serdes可以接受一些额外的配置以进行某种“自定义”的序列化/反序列化操作。
同时,Serdes也需要自行提供一个默认intent,因此当调用者不想自定义时,可以提供一个默认的配置(并且意味着kotlin中调用serialize/deserialize函数时可以不显示传Intent参数)
关于SerdesCallerIntent类型,将在下文介绍
TagConverter:
interface TagConverter<NbtRelatedType:Any, I:ConverterCallerIntent>{
fun defaultIntent():I
fun <V:Any> createTag(name: String?, value:V, typeToken: TypeToken<V>, intent: I = defaultIntent()): Tag<NbtRelatedType>?
fun <V:Any> toValue(tag: Tag<out Any>, typeToken: TypeToken<V>, intent: I = defaultIntent()): Pair<String?, V>?
} 和Serdes相似,一个新的泛型参数I:ConverterCallerIntent,以及createTag和toValue都额外添加ConverterCallerIntent参数,并指定默认值来自defaultIntent()
在此基础上,添加两个继承自Serdes/TagConverter的新抽象类(接口):HierarachicalSerdes,HierarachicalTagConverter
HierarchicalSerdes:
abstract class HierarchicalSerdes<NbtRelatedType:Any>():Serdes<NbtRelatedType, HierarchicalSerdesCallerIntent> {
val proxy: Serdes<Any, SerdesProxyCallerIntent>
override fun defaultIntent():HierarchicalSerdesIntent {TODO()}
} 在这里HierarchicalSerdes将Serdes的I:SerdesCallerIntent参数声明为HierarchicalSerdesCallerIntent,同时HierarchicalSerdes要求接受另一个HierarchicalSerdes作为proxy,其中proxy中I:SerdesIntent参数改为SerdesProxyCallerIntent
因此HierarchicalSerdesCallerIntent和SerdesProxyCallerIntent都继承于SerdesCallerIntent,关于二者详细信息将在下文介绍
同时抽象类实现defaultIntent方法,具体实现同样放于下文讲述
HierarchicalTagConverter:
abstract class HierarchicalTagConverter<NbtRelatedType:Any>()
:TagConverter<NbtRelatedType, HierarchicalConverterIntent> {
abstract var proxy:TagConverter<Any, HierarchicalConverterIntent>
override fun defaultIntent(): HierarchicalConverterIntent {
TODO()
}
} HierarchicalTagConverter基本结构和HierarchicalSerdes几乎一模一样,只是Intent变成HierarchicalConverterIntent,只是proxy使用的intent没有同样也是HierarchicalConverterIntent而不是单独的其他继承ConverterIntent类
可以注意到,HierarchicalSerdes和HierarchicalTagConverter都使用另一个相同类型但泛型参数为Any的proxy,原因在于HierarchicalSerdes 和 HierarchicalTagConverter本身的语义和功能:这二类对象面对的都是“嵌套”类型,而且只能处理传入的顶层嵌套类型
例如Converter传入的顶层参数为List,那么Converter只能处理List,但是对List内部元素的类型则无法处理,那么便需要委托给其他Converter协助处理
再例如某个Serdes只能接受Tag<Map>的Tag对象,但是其中Map里面的Key和value无法处理,那么同样需要委托给其他Serdes
而proxy便是一个通用的受委托对象,在功能上表现的和其他Serdes/Converter几乎一致,只不过其声明的泛型参数为Any,以表明其为一个“通用”的Serdes/Converter。至于proxy本身怎么做到“通用”的先暂时不表,只要理解为他被声明成一个“通用”Serdes/Converter即可
(在想出这个设计后,突然觉得背后的设计哲学有点类似于lunix系统中“一切皆文件”思想,脱离直观自然理解,从功能定义上高度抽象=w=)
接下来我们来解释SerdesCallerIntent和ConverterCallerIntent,同理先放声明:
SerdesCallerIntent和其子类:
class SerdesCallerIntent(var hasHead:Boolean)
class HierarchicalCallerIntent(hasHead:Boolean, parents:Deque&lt;Tag&lt;out Any&gt;):SerdesCallerIntent(hasHead)
class ProxyCallerIntent(
hasHead: Boolean,
parents: Deque&lt;Tag&lt;out Any&gt;&gt;,
var id:Byte? = null,
)
:HierarchicalSerdesCallerIntent(hasHead, parents) 参数解释:
hasHead:表明序列化/反序列化过程中是否涵盖tagHead。例如序列化过程中hasHead为false,则Serdes将只序列化TagValue,返回的ByteArray将不包括tag head信息(id和名字)。例如反序列化过程中hasHead为false,那么serdes将认为pointer将直接指向二进制TagValue信息,并且serdes将不负责二进制tag类型检查(因为不包括tag head,也就没有id信息)
parents:一个Deque(被当作栈使用),用于在 一次序列化/反序列化过程 记录所有父元素信息。例如在一次序列化过程中存在以下层级结构:ListTag->CompoundTag->ListTag->ListTag(A)->...,那么在序列化到ListTag(A)时,此时处理A的serdes将得到以下Deque记录:[ListTag,CompoundTag,ListTag]。
id:用于在序列化/反序列化过程中传递id信息。例如在反序列化过程中,proxy调用方希望只反序列化tag value二进制数据,那么通用serdes无法直接从指定的二进制数据获得tag id,此时便需要显式传递id信息给proxy。再比如在序列化过程中,调用者希望知道子元素tag的id信息,那么交给proxy序列化后,proxy将往intent写入序列化tag的id信息,以供调用者读取
hasHead参数满足我们 处理嵌套tag不一定包含tag head 的要求 ,parents则记录了上文信息,这样便可以检查循环引用和限制nbt结构深度;id参数则是在明确调用者/受委托者分工后,提供给双方所需信息的通道:序列化过程中调用者无需知道子元素tag信息,直接交给被委托者处理,并由被委托者给出相应信息;反序列化过程中受委托者不需要(有时也无法)自行弄清楚需要反序列化哪种tag,需要委托者显示指定。
ConverterCallerIntent和其子类:
class ConverterCallerIntent(ignoreTypeToken:Boolean = false)
class HierarchicalConverterIntent(
var parents:Deque&lt;Any&gt;,
ignoreTypeToken:Boolean = false
):ConverterCallerIntent(ignoreTypeToken) 这两个Intent相比SerdesCallerIntent要简单一些。ConverterCallerIntent中ignoreTypeToken标明让受代理的Converter忽略TypeToken,按照其默认方式转换;HierarchicalConverterIntent的parents参数和HiearachicalSerdesCallerIntent中的parents参数功能相同,提供上文所有父元素信息。
基于以上接口,我们开始实现List Tag和Compound Tag相关的实现
List Tag-> Tag<List>
class ListTag&lt;NbtRelatedType:Any&gt;(
override val name: String?,
override val value: MutableList&lt;Tag&lt;NbtRelatedType&gt;&gt;,
) : Tag&lt;MutableList&lt;Tag&lt;NbtRelatedType&gt;&gt;&gt;() {
constructor(name:String?=null):this(name, mutableListOf())
fun &lt;T:Tag&lt;NbtRelatedType&gt;&gt; add(tag:T):Boolean {
return value.add(tag)
}
} Compound Tag -> Tag<Collection>
typealias AnyCompound = Collection&lt;Tag&lt;out Any&gt;&gt;
class CompoundTag(override val name: String?, override val value: AnyCompound): Tag&lt;AnyCompound&gt;() {
constructor(name:String?=null):this(name, mutableSetOf())
operator fun add(value:Tag&lt;out Any&gt;) {
this.value.add(value)
}
} 两个实现十分简单,直接通过MutableList<NbtRelatedType>和MutableMap<String, Any>表示对应的nbt类型的值结构,并提供几个简单short cut函数
接下来是以上两种Tag的Serdes实现,首先是一个通用DefaultHierarchicalSerdes,具体功能参见其中注释:
abstract class DefaultHierarchicalSerdes&lt;NbtRelatedType:Any&gt;(
override val id: Byte, override val valueTypeToken: TypeToken&lt;NbtRelatedType&gt;
):HierarchicalSerdes&lt;NbtRelatedType&gt; {
// 一些抽象方法
// 序列化tag值方法
abstract fun serializeValue(value:NbtRelatedType, context: HierarchicalSerdesCallerIntent):ByteArray
// 反序列化成tag值方法
abstract fun deserializeToValue(data: ByteArray, start:Int, context: HierarchicalSerdesCallerIntent):Pair&lt;NbtRelatedType, Int&gt;
// 创建Tag方法
abstract fun createTag(name:String?, value:NbtRelatedType):Tag&lt;NbtRelatedType&gt;
// 默认intent:hasHead=true,以及创建一个新的Deque记录上文父元素
override fun defaultSerdesIntent(): HierarchicalSerdesCallerIntent {
return HierarchicalSerdesCallerIntent(true, ArrayDeque())
}
override fun serialize(tag: Tag&lt;out NbtRelatedType&gt;, intent: HierarchicalSerdesCallerIntent):ByteArray {
val valueBits = serializeValue(tag.value, intent)
val name = tag.name
// 根据tag.name是否为空,选择输出不同的ByteArray
// getTagBits:拼接tag head和tag value的工具方法
return if (name==null) valueBits else SerdesTool.getTagBits(id, name, valueBits)
}
override fun deserialize(data: ByteArray, start: Int, intent: HierarchicalSerdesCallerIntent):Pair&lt;Tag&lt;NbtRelatedType&gt;, Int&gt; {
var pointer = start
// 如果intent hasHead为true,检查二进制数据tag id格式是否正确,并反序列化tag head
val name = if (intent.hasHead) {
SerdesTool.checkNbtFormat(data, pointer, id)
pointer += TagIdPayload
val metaName = SerdesTool.readNbtName(data, pointer)
val name = metaName.first
pointer = metaName.second
name
} else null // 否则不做检查
val metaValue = deserializeToValue(data, pointer, intent)
val value = metaValue.first
pointer = metaValue.second
return Pair(createTag(name, value), pointer)
}
} 然后ListTagSerdes继承该DefaultHierarchicalSerdes:
class ListTagSerdes(override val proxy: Serdes&lt;Any, ProxyCallerIntent&gt;)
:DefaultHierarchicalSerdes&lt;AnyTagList&gt;(9, TypeToken.of(MutableList::class.java) as TypeToken&lt;AnyTagList&gt;) {
override fun createTag(name: String?, value: AnyTagList): Tag&lt;AnyTagList&gt; {
return ListTag(name, value)
}
override fun serializeValue(value: AnyTagList, intent: HierarchicalSerdesCallerIntent): ByteArray {
val head = ByteArray(TagIdPayload + ArraySizePayload)
val elementBitsList = ArrayList&lt;ByteArray&gt;() // 缓存子元素的二进制数据
var valueBitsLen = 0 // 记录tag value二进制数据大小
var elementId:Byte = 0 // 记录子元素id
var actualElementNum = 0 // 实际有效子元素数量
// 序列化方法:选取第一个有效子元素,并获取其id作为elementId
// 然后将id作为该列表的有效id
// 之后如果遇见不符合的id,直接抛弃
for (tags in value) {
val proxyIntent = ProxyCallerIntent(false, intent.parents)
val metaElementBits = proxy.serialize(tags, proxyIntent)
if (elementId==(-1).toByte()) {
elementId = proxyIntent.id!!
head[0] = elementId // 设置tag head 子元素id
}
if (elementId != proxyIntent.id) continue
elementBitsList.add(metaElementBits)
valueBitsLen += metaElementBits.size
actualElementNum += 1
}
// 复制列表大小到head
System.arraycopy(actualElementNum.toBytes(), 0, head, TagIdPayload, ArraySizePayload)
val resBits = ByteArray(head.size+valueBitsLen)
var pointer = 0
// 复制tag head
System.arraycopy(head, 0, resBits, pointer, head.size)
pointer += head.size
// 复制每个子元素的二进制数据
elementBitsList.onEach { elementBits -&gt;
System.arraycopy(elementBits, 0, resBits, pointer, elementBits.size)
pointer += elementBits.size
}
return resBits
}
override fun deserializeToValue(data: ByteArray, start: Int, intent: HierarchicalSerdesCallerIntent): Pair&lt;AnyTagList, Int&gt; {
// 获取子元素id
var pointer = start
val elementId = data[start]
pointer += TagIdPayload
// 获取列表大小
val size = data.toBasic(pointer, 0)
pointer += ArraySizePayload
// 如果列表大小为空,返回空list
if (size==0) {
val nbtlist = mutableListOf&lt;Tag&lt;Any&gt;&gt;()
return Pair(nbtlist, pointer)
}
val nbtlist = mutableListOf&lt;Tag&lt;Any&gt;&gt;()
for (i in 0 until size) {
val newIntent = ProxyCallerIntent(false, intent.parents, elementId)
val metaTag = proxy.deserialize(data, pointer, newIntent)
nbtlist.add(metaTag.first)
pointer = metaTag.second
}
return Pair(nbtlist, pointer)
}
} 然后是CompoundTagSerdes:
class CompoundTagSerdes(override val proxy:Serdes&lt;Any, ProxyCallerIntent&gt;)
: DefaultHierarchicalSerdes&lt;AnyCompound&gt;(10, TypeToken.of(Collection::class.java) as TypeToken&lt;AnyCompound&gt;) {
override fun createTag(name: String?, value: AnyCompound): Tag&lt;AnyCompound&gt; {
return CompoundTag(name, value)
}
override fun serializeValue(value: AnyCompound, intent: HierarchicalSerdesCallerIntent): ByteArray {
val subTagBitsList = ArrayList&lt;ByteArray&gt;()
var valueBitsLen = 0
value.onEach { subTag-&gt;
checkNotNull(subTag.name) //tag 名字不能为空
// intent:序列化整个tag,包含tag head, 直接传递parents数据
val proxyIntent = ProxyCallerIntent(true, intent.parents)
val metaValue = proxy.serialize(subTag, proxyIntent)
valueBitsLen += metaValue.size
subTagBitsList.add(metaValue)
}
val resBits = ByteArray(valueBitsLen+1)
var pointer = 0
subTagBitsList.onEach {
System.arraycopy(it, 0, resBits, pointer, it.size)
pointer += it.size
}
resBits[pointer] = IdTagEnd
return resBits
}
override fun deserializeToValue(data: ByteArray, start: Int, intent: HierarchicalSerdesCallerIntent): Pair&lt;AnyCompound, Int&gt; {
var pointer = start
var subTagId = data[pointer]
val compound = mutableSetOf&lt;Tag&lt;out Any&gt;&gt;()
while (subTagId != IdTagEnd) { // 当id不为0(TagEnd),持续反序列化子元素
val proxyIntent = ProxyCallerIntent(true, intent.parents, subTagId)
val metaTag = proxy.deserialize(data, pointer, proxyIntent)
compound.add(metaTag.first)
pointer = metaTag.second
if (pointer &gt;= data.size) break
subTagId = data[pointer]
}
return Pair(compound, pointer+TagIdPayload)
}
} 然后是他们的代理Serdes实现:
DefaultSerdesProxy:
class DefaultSerdesProxy():Serdes&lt;Any, ProxyCallerIntent&gt; {
// 这里一点小问题:Proxy不应该有id和valueTypeToken
override val id: Byte
get() = TODO(&quot;Not yet implemented&quot;)
override val valueTypeToken: TypeToken&lt;Any&gt;
get() = TODO(&quot;Not yet implemented&quot;)
// serdes map,根据id找到对应的serdes
val serdesMap:MutableMap&lt;Byte, Serdes&lt;out Any,out SerdesCallerIntent&gt;&gt;
= HashMap&lt;Byte, Serdes&lt;out Any,out SerdesCallerIntent&gt;&gt;()
fun registerSerdes(id:Byte, serdes:Serdes&lt;out Any,out SerdesCallerIntent&gt;) {
this.serdesMap[id] = serdes
}
constructor(serdesInstances:List&lt;Serdes&lt;Any, SerdesCallerIntent&gt;&gt;):this() {
for (serdes in serdesInstances) serdesMap[serdes.id] = serdes
}
override fun defaultSerdesIntent(): ProxyCallerIntent {
return ProxyCallerIntent(true, ArrayDeque())
}
override fun serialize(tag: Tag&lt;out Any&gt;, intent: ProxyCallerIntent): ByteArray {
val value = tag.value
val typeToken = value::class.java.let { TypeToken.of(it) }
// 根据传入的typeToken找到合适的serdes
val serdes = serdesMap.entries.find { entry-&gt;
TypeCheckTool.isCastable(typeToken, entry.value.valueTypeToken)
}?.value ?: throw NullPointerException(&quot;can not find serdes with type ${typeToken.type}.&quot;)
// 检查已遍历的树深度
if (intent.parents.size &gt; defaultMaxTreeDepth-1) {
throw MaxNbtTreeDepthException(intent.parents.size)
}
// 检查引用循环
intent.parents.onEach { if (tag === it) throw CircularReferenceException(tag) }
// 将当前tag入栈
intent.parents.push(tag)
val castedSerdes = serdes as Serdes&lt;Any, SerdesCallerIntent&gt;
val bits = castedSerdes.serialize(tag, intent)
intent.parents.pop() // tag 出栈
intent.id = serdes.id // 返回intent消息:设置serdes对应的tag类型id
return bits
}
override fun deserialize(data: ByteArray, start: Int, intent: ProxyCallerIntent): Pair&lt;Tag&lt;Any&gt;, Int&gt; {
// 如果id为null,意味着caller不指定id,serdes将从二进制数据中解析出id
// 如果出现caller不指定id,hasHead为false,会抛出异常
if (intent.id == null &amp;&amp; !intent.hasHead) throw IllegalArgumentException()
if (intent.id == null) {
intent.id = data[start]
// TODO: print debug info (auto find id by binary data)
}
val serdes = serdesMap[intent.id!!]?: throw IllegalArgumentException(&quot;can not find serdes with related id $id&quot;)
serdes as Serdes&lt;Any, SerdesCallerIntent&gt; // runtime cast
return serdes.deserialize(data, start, intent)
}
} 以上我们实现了从Tag-Serdes部分,接下来是Converter部分。Converter部分比较复杂,一个Tag<List>可能对应多个Converter(ArrayConverter,ListConverter),我们只举一种Converter来说明,比如Tag<List>->ListTypeConverter(转成List形式),比如Tag<Collection>->MapTypeConverter(转成Map形式)
ListTypeConverter:
class ListTypeConverter(override var proxy: TagConverter&lt;Any, NestIntent&gt;)
: HierarchicalTagConverter&lt;AnyTagList&gt;() {
private val listType = TypeToken.of(List::class.java)
private val listGenericType = List::class.java.typeParameters[0]
override fun &lt;V : Any&gt; createTag(name: String?, value: V, typeToken: TypeToken&lt;V&gt;, intent:NestIntent): Tag&lt;AnyTagList&gt;? {
// 检查值是否继承自List
if(!typeToken.isSubtypeOf(listType)) return null
value as List&lt;Any&gt;
// 尝试获取List&lt;E&gt;的E实际类型,例如List&lt;Int&gt;将拿到Int
val declaredElementType = typeToken.resolveType(listGenericType) as TypeToken&lt;Any&gt;
val size = value.size
// 尝试获取第一个元素的类型(如果列表为空,则为null)
val firstElementType = if(size&gt;0) TypeToken.of(value[0]::class.java) else null
val list:AnyTagList = mutableListOf()
value.onEach { element-&gt;
// 尝试用元素转成泛型E声明的实际类型,或者是第一个元素的类型作为typeToken
// 如果都不行,返回null
val convertedValue = proxy.createTag(null, element, declaredElementType, intent)
?:firstElementType?.let { proxy.createTag(null, element, it as TypeToken&lt;Any&gt;, NestIntent(intent.parents, true)) }
?: return@onEach
list.add(convertedValue)
}
return ListTag(name, list)
}
override fun &lt;V : Any&gt; toValue(tag: Tag&lt;out Any&gt;, typeToken: TypeToken&lt;V&gt;, intent:NestIntent): Pair&lt;String?, V&gt;? {
// 检查Tag value为List
if (tag.value !is List&lt;*&gt;) return null
val value = tag.value as AnyTagList
// 如果intent ignoreTypeToken 为 true, 则使用默认TypeToken作为返回value 的类型
val actualTypeToken = if (intent.ignoreTypeToken) listType else typeToken
if (!actualTypeToken.isSubtypeOf(listType)) return null
val declaredElementType = actualTypeToken.resolveType(listGenericType) as TypeToken&lt;Any&gt;
//尝试从实际typeToken创建新List对象
val list = newList(actualTypeToken)?: return null
value.onEach { elementTagValue-&gt;
val elementType = if(value.size&gt;0) {
value.get(0)?.let{TypeToken.of(it.value::class.java)}
}else null
elementType as TypeToken&lt;Any&gt;
val metaTargetElement = proxy.toValue(elementTagValue, declaredElementType, intent)
?:proxy.toValue(elementTagValue, elementType, NestIntent(intent.parents, true))
?: return@onEach
list.add(metaTargetElement.second)
}
return Pair(tag.name, list as V)
}
fun newList(typeToken: TypeToken&lt;*&gt;):MutableList&lt;Any&gt;? {
val rawType = typeToken.rawType
// 如果typeToken的rawType是抽象类或接口(且已知继承自List),返回mutableListOf()
if (rawType.isInterface || Modifier.isAbstract(rawType.modifiers) &amp;&amp; listType.isSupertypeOf(rawType)) return mutableListOf()
// 尝试寻找参数为空的构造函数用反射并构造新对象,否则返回null
val constructors = rawType.constructors
val constructor = constructors.find { constructor-&gt;
constructor.parameters.isEmpty()
}?: return null
return constructor.newInstance() as MutableList&lt;Any&gt;
}
} MapTypeConverter:
class MapTypeConverter
(override var proxy: TagConverter&lt;Any,NestIntent&gt;)
:HierarchicalTagConverter&lt;AnyCompound&gt;() {
private val mapTypeToken = TypeToken.of(Map::class.java)
private val mapValueGenericType = Map::class.java.typeParameters[1]
override fun &lt;V:Any&gt; createTag
(name: String?, value: V, typeToken: TypeToken&lt;V&gt;, intent: NestIntent)
:Tag&lt;AnyCompound&gt;?
{
if (!typeToken.isSubtypeOf(mapTypeToken)) return null
//获取typetoken&lt;Map&lt;K,V&gt;&gt; V泛型参数的声明类型
val declaredValueTypeToken = typeToken.resolveType(mapValueGenericType) as TypeToken&lt;Any&gt;
value as Map&lt;String, Any&gt;
val set:AnyCompound = mutableSetOf()
value.onEach {
val subValue = it.value
val tag = proxy.createTag(it.key, subValue, declaredValueTypeToken, NestIntent(intent.parents, false))
?: proxy.createTag(it.key, subValue, TypeToken.of(subValue::class.java) as TypeToken&lt;Any&gt;, NestIntent(intent.parents, true))
?: return@onEach
set.add(tag)
}
return CompoundTag(name, set)
}
override fun &lt;V:Any&gt; toValue(tag: Tag&lt;out Any&gt;, typeToken: TypeToken&lt;V&gt;, intent: NestIntent): Pair&lt;String?, V&gt;? {
// 检查是否是AnyCompound
if (tag.value !is Collection&lt;*&gt;) return null
val value = tag.value as Collection&lt;Tag&lt;out Any&gt;&gt;
// 如果ignoreTypeToken 为 true, Converter自行决定返回类型
val actualTypeTokenIn = if (intent.ignoreTypeToken) mapTypeToken else typeToken
// 检查TypeTokenIn是否是Map的子类
if (!actualTypeTokenIn.isSubtypeOf(mapTypeToken)) return null
// 尝试获取 TypeToken中 Map&lt;K,V&gt; 中 泛型V的声明
val declaredValueTypeToken = actualTypeTokenIn.resolveType(mapValueGenericType) as TypeToken&lt;Any&gt;
val newMap = newMapInstance(actualTypeTokenIn)?: return null
newMap as MutableMap&lt;String, Any&gt;
value.onEach { subTag-&gt;
checkNotNull(subTag.name)
val subTagValueTypeToken = TypeToken.of(subTag.value::class.java) as TypeToken&lt;Any&gt;
// 尝试用V声明的类型,如果proxy返回null,则尝试ignoreTypeToken,
val res = proxy.toValue(subTag, declaredValueTypeToken, NestIntent(intent.parents, false))?:
proxy.toValue(subTag, subTagValueTypeToken, NestIntent(intent.parents, true)) ?: return@onEach
newMap[subTag.name!!] = res.second
}
return Pair(tag.name, newMap as V)
}
fun &lt;V&gt; newMapInstance(typeToken: TypeToken&lt;V&gt;):V? {
val rawType = typeToken.rawType
// (继承自Map的)抽象类/接口,则直接返回mutableMapOf()
if (rawType.isInterface || Modifier.isAbstract(rawType.modifiers) &amp;&amp; mapTypeToken.isSupertypeOf(rawType)) return mutableMapOf&lt;String, Any&gt;() as V
// 尝试获取空参数构造函数并以此反射创建对象,否则返回空
val constructors = typeToken.rawType.constructors
val constructor = constructors.find { constructor-&gt;
constructor.parameters.isEmpty()
}?: return null
return constructor.newInstance() as V
}
} 然后是HierarchicalConverter们的代理类
class DefaultConverterProxy :HierarchicalTagConverter&lt;Any&gt;() {
// Proxy查找合适Converter的方式:遍历Converter列表,
// 对每个Converter尝试createTag/toValue操作
// 若结果非空,则返回
// 因此,列表的顺序决定了Converter优先级,如果有两个Converter可以处理同一种对象,
// 则排在前面的Converter会进行处理
override var proxy: TagConverter&lt;Any, HierarchicalConverterCallerIntent&gt; = this
private val delegateList = ArrayList&lt;TagConverter&lt;Any, ConverterCallerIntent&gt;&gt;()
//添加受代理的Converter
fun registerDelegateLast(delegate: TagConverter&lt;*,*&gt;) {
delegateList.add(delegate as TagConverter&lt;Any,ConverterCallerIntent&gt;)
}
override fun &lt;V : Any&gt; createTag(name: String?, value: V, typeToken: TypeToken&lt;V&gt;, intent:HierarchicalConverterCallerIntent): Tag&lt;Any&gt;? {
// 检查嵌套深度
if (intent.parents.size &gt; defaultMaxTreeDepth-1) throw MaxNbtTreeDepthException(intent.parents.size)
// 检查循环引用,如果发现循环引用,则返回空
// TODO:添加其他策略,比如发现循环引用时抛出异常
val circularRef = intent.parents.find { value===it }
if (circularRef != null) return null
intent.parents.push(value)
var tag:Tag&lt;Any&gt;? = null
for (delegate in delegateList) {
tag = delegate.createTag(name, value, typeToken, intent)?:continue
if (tag != null) break
}
intent.parents.pop()
return tag
}
override fun &lt;V : Any&gt; toValue(tag: Tag&lt;out Any&gt;, typeToken: TypeToken&lt;V&gt;, intent: HierarchicalConverterCallerIntent): Pair&lt;String?, V&gt;? {
for (delegate in delegateList) {
return delegate.toValue(tag, typeToken, intent) ?: continue
}
return null
}
} Converter代理类的方法则比Serdes代理类复杂。这是来自于Gson的查找方法,遍历一个列表,并交给第一个可处理的Converter处理
由此,通过以上实现我们可以完成对嵌套类型的序列化/反序列化应用,例如对Map的序列化/反序列化
// 创建一个包含所有proxy可处理的值的map,例如Map,List,Int,String,ByteArray,...
val map:Map&lt;String,Any&gt; = someLegalMap()
// 一个可以处理以上所有值的Converter proxy
val converterProxy:DefaultConverterProxy = someConverterProxy()
val mapTypeConverter:TagConverter&lt;Collection&lt;*&gt;,HierarchicalConverterCallerIntent&gt;
= MapTypeConverter(converterProxy)
val serdesProxy:DefaultSerdesProxy = someSerdesProxy()
val compTagSerdes:Serdes&lt;Collection&lt;*&gt;, HierarchicalSerdesCallerIntent&gt;
= CompoundTagSerdes(serdesProxy)
val tag:Tag&lt;Collection&lt;*&gt;&gt; = mapTypeConverter.createTag(&quot;some map&quot;, map, object:TypeToken&lt;Map&lt;String, Any&gt;())
val bits:ByteArray = compTagSerdes.serialize(tag)
val deserializedTag = compTagSerdes.deserialize(bits, 0)
assertTrue(tag, deserializedTag) //序列化-&gt;反序列化后的tag和原tag相等
val map2 = mapTypeConverter.toValue(deserializedTag, object:TypeToken&lt;Map&lt;String, Any&gt;())
assertTrue(map, map2) // 最终的map和原map相等
并且同样的,SerdesProxy和TagConverterProxy也应该能完成以上工作。事实上这两个代理应该可以完成目前所有实现的Converter和Serdes的工作,只要正确地把其他Converter和Serdes放进这两个代理中即可。
以上便是目前我关于如何实现嵌套类型Tag的序列化/反序列化功能的目前所有开发结果,下一篇我应该会聊聊开发过程数次被推翻重改的那些上层接口们(以及无数代码代码改动的心酸泪目史QwQ)