从零开始的Minecraft - Nbt序列化库开发:嵌套类型Nbt开发(1) - 顶层接口改动
疑似叉叉星来的鹩八哥
2023年01月16日 15:43
收录于文集
共10篇

项目介绍:从零开始的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:

代码块
JavaScript
自动换行
复制代码
abstract class Tag<NbtRelatedType:Any> {
  val name:String?
  val value:NbtRelatedType
}
复制成功

基本没有改动,除了name的类型声明改为(String?),意思为可为空(null),此改动为了符合第三条需求,嵌套的子元素Tag不一定有Tag Head

Serdes:

代码块
JavaScript
自动换行
复制代码
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: 

代码块
JavaScript
自动换行
复制代码
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:

代码块
JavaScript
自动换行
复制代码
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: 

代码块
JavaScript
自动换行
复制代码
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和其子类:

代码块
JavaScript
自动换行
复制代码
class SerdesCallerIntent(var hasHead:Boolean)

class HierarchicalCallerIntent(hasHead:Boolean, parents:Deque<Tag<out Any>):SerdesCallerIntent(hasHead)

class ProxyCallerIntent(
        hasHead: Boolean,
        parents: Deque<Tag<out Any>>,
        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和其子类:

代码块
JavaScript
自动换行
复制代码
class ConverterCallerIntent(ignoreTypeToken:Boolean = false)

class HierarchicalConverterIntent(
  var parents:Deque<Any>,
  ignoreTypeToken:Boolean = false
):ConverterCallerIntent(ignoreTypeToken)
复制成功

这两个Intent相比SerdesCallerIntent要简单一些。ConverterCallerIntent中ignoreTypeToken标明让受代理的Converter忽略TypeToken,按照其默认方式转换;HierarchicalConverterIntent的parents参数和HiearachicalSerdesCallerIntent中的parents参数功能相同,提供上文所有父元素信息。

具体实现:

基于以上接口,我们开始实现List Tag和Compound Tag相关的实现

List Tag-> Tag<List>

代码块
JavaScript
自动换行
复制代码
class ListTag<NbtRelatedType:Any>(
        override val name: String?,
        override val value: MutableList<Tag<NbtRelatedType>>,
        ) : Tag<MutableList<Tag<NbtRelatedType>>>() {
    constructor(name:String?=null):this(name, mutableListOf())

    fun <T:Tag<NbtRelatedType>> add(tag:T):Boolean {
        return value.add(tag)
    }
}
复制成功

Compound Tag -> Tag<Collection>

代码块
JavaScript
自动换行
复制代码
typealias AnyCompound = Collection<Tag<out Any>>
class CompoundTag(override val name: String?, override val value: AnyCompound): Tag<AnyCompound>() {
  constructor(name:String?=null):this(name, mutableSetOf())  
  
  operator fun add(value:Tag<out Any>) {
    this.value.add(value)
  }
}
复制成功

两个实现十分简单,直接通过MutableList<NbtRelatedType>和MutableMap<String, Any>表示对应的nbt类型的值结构,并提供几个简单short cut函数

接下来是以上两种Tag的Serdes实现,首先是一个通用DefaultHierarchicalSerdes,具体功能参见其中注释:

代码块
JavaScript
自动换行
复制代码
abstract class DefaultHierarchicalSerdes<NbtRelatedType:Any>(
        override val id: Byte, override val valueTypeToken: TypeToken<NbtRelatedType>
):HierarchicalSerdes<NbtRelatedType> {
	 // 一些抽象方法
  	 // 序列化tag值方法
    abstract fun serializeValue(value:NbtRelatedType, context: HierarchicalSerdesCallerIntent):ByteArray
    // 反序列化成tag值方法
    abstract fun deserializeToValue(data: ByteArray, start:Int, context: HierarchicalSerdesCallerIntent):Pair<NbtRelatedType, Int>
    // 创建Tag方法
    abstract fun createTag(name:String?, value:NbtRelatedType):Tag<NbtRelatedType>

    // 默认intent:hasHead=true,以及创建一个新的Deque记录上文父元素
    override fun defaultSerdesIntent(): HierarchicalSerdesCallerIntent {
        return HierarchicalSerdesCallerIntent(true, ArrayDeque())
    }
	 
    override fun serialize(tag: Tag<out NbtRelatedType>, 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<Tag<NbtRelatedType>, Int> {
        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:

代码块
JavaScript
自动换行
复制代码
class ListTagSerdes(override val proxy: Serdes<Any, ProxyCallerIntent>)
        :DefaultHierarchicalSerdes<AnyTagList>(9, TypeToken.of(MutableList::class.java) as TypeToken<AnyTagList>) {
	override fun createTag(name: String?, value: AnyTagList): Tag<AnyTagList> {
   		return ListTag(name, value)
   }

   override fun serializeValue(value: AnyTagList, intent: HierarchicalSerdesCallerIntent): ByteArray {
     val head = ByteArray(TagIdPayload + ArraySizePayload)

     val elementBitsList = ArrayList<ByteArray>() // 缓存子元素的二进制数据
     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 ->
       System.arraycopy(elementBits, 0, resBits, pointer, elementBits.size)
       pointer += elementBits.size
     }
     return resBits
   }

  override fun deserializeToValue(data: ByteArray, start: Int, intent: HierarchicalSerdesCallerIntent): Pair<AnyTagList, Int> {
    // 获取子元素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<Tag<Any>>()
      return Pair(nbtlist, pointer)
    }

    val nbtlist = mutableListOf<Tag<Any>>()
    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:

代码块
JavaScript
自动换行
复制代码
class CompoundTagSerdes(override val proxy:Serdes<Any, ProxyCallerIntent>)
        : DefaultHierarchicalSerdes<AnyCompound>(10, TypeToken.of(Collection::class.java) as TypeToken<AnyCompound>) {

	override fun createTag(name: String?, value: AnyCompound): Tag<AnyCompound> {
      return CompoundTag(name, value)
   }

	override fun serializeValue(value: AnyCompound, intent: HierarchicalSerdesCallerIntent): ByteArray {
      val subTagBitsList = ArrayList<ByteArray>()
      var valueBitsLen = 0
      value.onEach { subTag->
        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<AnyCompound, Int> {
      var pointer = start
      var subTagId = data[pointer]
      val compound = mutableSetOf<Tag<out Any>>()
		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 >= data.size) break
          subTagId = data[pointer]
        }
    	return Pair(compound, pointer+TagIdPayload)
	}
}
复制成功

然后是他们的代理Serdes实现:

DefaultSerdesProxy:

代码块
JavaScript
自动换行
复制代码
class DefaultSerdesProxy():Serdes<Any, ProxyCallerIntent> {

    // 这里一点小问题:Proxy不应该有id和valueTypeToken
    override val id: Byte
        get() = TODO("Not yet implemented")
    override val valueTypeToken: TypeToken<Any>
        get() = TODO("Not yet implemented")

	 // serdes map,根据id找到对应的serdes
    val serdesMap:MutableMap<Byte, Serdes<out Any,out SerdesCallerIntent>>
      = HashMap<Byte, Serdes<out Any,out SerdesCallerIntent>>()

    fun registerSerdes(id:Byte, serdes:Serdes<out Any,out SerdesCallerIntent>) {
        this.serdesMap[id] = serdes
    }

    constructor(serdesInstances:List<Serdes<Any, SerdesCallerIntent>>):this() {
        for (serdes in serdesInstances) serdesMap[serdes.id] = serdes
    }

    override fun defaultSerdesIntent(): ProxyCallerIntent {
        return ProxyCallerIntent(true, ArrayDeque())
    }

    override fun serialize(tag: Tag<out Any>, intent: ProxyCallerIntent): ByteArray {
        val value = tag.value
        val typeToken = value::class.java.let { TypeToken.of(it) }
        // 根据传入的typeToken找到合适的serdes
        val serdes = serdesMap.entries.find { entry->
            TypeCheckTool.isCastable(typeToken, entry.value.valueTypeToken) 
        }?.value ?: throw NullPointerException("can not find serdes with type ${typeToken.type}.")
        // 检查已遍历的树深度
        if (intent.parents.size > 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<Any, SerdesCallerIntent>
        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<Tag<Any>, Int> {
        // 如果id为null,意味着caller不指定id,serdes将从二进制数据中解析出id
        // 如果出现caller不指定id,hasHead为false,会抛出异常
        if (intent.id == null && !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("can not find serdes with related id $id")
        serdes as Serdes<Any, SerdesCallerIntent> // 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:

代码块
JavaScript
自动换行
复制代码
class ListTypeConverter(override var proxy: TagConverter<Any, NestIntent>)
        : HierarchicalTagConverter<AnyTagList>() {
	private val listType = TypeToken.of(List::class.java)
	private val listGenericType = List::class.java.typeParameters[0]

	override fun <V : Any> createTag(name: String?, value: V, typeToken: TypeToken<V>, intent:NestIntent): Tag<AnyTagList>? {
      // 检查值是否继承自List
      if(!typeToken.isSubtypeOf(listType)) return null
      value as List<Any>
      // 尝试获取List<E>的E实际类型,例如List<Int>将拿到Int
      val declaredElementType = typeToken.resolveType(listGenericType) as TypeToken<Any>

		val size = value.size
		// 尝试获取第一个元素的类型(如果列表为空,则为null)
		val firstElementType = if(size>0) TypeToken.of(value[0]::class.java) else null
		val list:AnyTagList = mutableListOf()
		value.onEach { element->
          // 尝试用元素转成泛型E声明的实际类型,或者是第一个元素的类型作为typeToken
          // 如果都不行,返回null
          val convertedValue = proxy.createTag(null, element, declaredElementType, intent) 
          	?:firstElementType?.let { proxy.createTag(null, element, it as TypeToken<Any>, NestIntent(intent.parents, true)) } 
          	?: return@onEach
          list.add(convertedValue)
		}
		return ListTag(name, list)
	}

	override fun <V : Any> toValue(tag: Tag<out Any>, typeToken: TypeToken<V>, intent:NestIntent): Pair<String?, V>? {
		// 检查Tag value为List
		if (tag.value !is List<*>) 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<Any>
		//尝试从实际typeToken创建新List对象
		val list = newList(actualTypeToken)?: return null
        
		value.onEach { elementTagValue->
			val elementType = if(value.size>0) {
         	value.get(0)?.let{TypeToken.of(it.value::class.java)}
         }else null
			elementType as TypeToken<Any>
			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<*>):MutableList<Any>? {
      val rawType = typeToken.rawType
      // 如果typeToken的rawType是抽象类或接口(且已知继承自List),返回mutableListOf()
      if (rawType.isInterface || Modifier.isAbstract(rawType.modifiers) && listType.isSupertypeOf(rawType)) return mutableListOf()
      // 尝试寻找参数为空的构造函数用反射并构造新对象,否则返回null
      val constructors = rawType.constructors
      val constructor = constructors.find { constructor->
          constructor.parameters.isEmpty()
      }?: return null
      return constructor.newInstance() as MutableList<Any>
	}
}
复制成功

MapTypeConverter:

代码块
JavaScript
自动换行
复制代码
class MapTypeConverter
	(override var proxy: TagConverter<Any,NestIntent>)
    :HierarchicalTagConverter<AnyCompound>() {
	private val mapTypeToken = TypeToken.of(Map::class.java)
	private val mapValueGenericType = Map::class.java.typeParameters[1]
	
	override fun <V:Any> createTag
		(name: String?, value: V, typeToken: TypeToken<V>, intent: NestIntent)
		:Tag<AnyCompound>? 
	{
		if (!typeToken.isSubtypeOf(mapTypeToken)) return null
		//获取typetoken<Map<K,V>> V泛型参数的声明类型
		val declaredValueTypeToken = typeToken.resolveType(mapValueGenericType) as 			TypeToken<Any>
		value as Map<String, Any>
		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<Any>, NestIntent(intent.parents, true))
				?: return@onEach
			set.add(tag)
		}
		return CompoundTag(name, set)
    }

	override fun <V:Any> toValue(tag: Tag<out Any>, typeToken: TypeToken<V>, intent: NestIntent): Pair<String?, V>? {
		// 检查是否是AnyCompound
		if (tag.value !is Collection<*>) return null
		val value = tag.value as Collection<Tag<out Any>>
		// 如果ignoreTypeToken 为 true, Converter自行决定返回类型
		val actualTypeTokenIn = if (intent.ignoreTypeToken) mapTypeToken else typeToken
		// 检查TypeTokenIn是否是Map的子类
		if (!actualTypeTokenIn.isSubtypeOf(mapTypeToken)) return null
		// 尝试获取 TypeToken中 Map<K,V> 中 泛型V的声明
		val declaredValueTypeToken = actualTypeTokenIn.resolveType(mapValueGenericType) as TypeToken<Any>
		val newMap = newMapInstance(actualTypeTokenIn)?: return null
		newMap as MutableMap<String, Any>
		value.onEach { subTag->
			checkNotNull(subTag.name)
			val subTagValueTypeToken = TypeToken.of(subTag.value::class.java) as TypeToken<Any>
			// 尝试用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 <V> newMapInstance(typeToken: TypeToken<V>):V? {
		val rawType = typeToken.rawType
		// (继承自Map的)抽象类/接口,则直接返回mutableMapOf()
		if (rawType.isInterface || Modifier.isAbstract(rawType.modifiers) && mapTypeToken.isSupertypeOf(rawType)) return mutableMapOf<String, Any>() as V
		// 尝试获取空参数构造函数并以此反射创建对象,否则返回空
		val constructors = typeToken.rawType.constructors
		val constructor = constructors.find { constructor->
			constructor.parameters.isEmpty()
		}?: return null
		return constructor.newInstance() as V
	}
}
复制成功

然后是HierarchicalConverter们的代理类

代码块
JavaScript
自动换行
复制代码
class DefaultConverterProxy :HierarchicalTagConverter<Any>() {
    // Proxy查找合适Converter的方式:遍历Converter列表,
    // 对每个Converter尝试createTag/toValue操作
    // 若结果非空,则返回
    // 因此,列表的顺序决定了Converter优先级,如果有两个Converter可以处理同一种对象,
    // 则排在前面的Converter会进行处理
    override var proxy: TagConverter<Any, HierarchicalConverterCallerIntent> = this

    private val delegateList = ArrayList<TagConverter<Any, ConverterCallerIntent>>()

    //添加受代理的Converter
    fun registerDelegateLast(delegate: TagConverter<*,*>) {
        delegateList.add(delegate as TagConverter<Any,ConverterCallerIntent>)
    }

    override fun <V : Any> createTag(name: String?, value: V, typeToken: TypeToken<V>, intent:HierarchicalConverterCallerIntent): Tag<Any>? {
        // 检查嵌套深度
        if (intent.parents.size > 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<Any>? = null
        for (delegate in delegateList) {
            tag = delegate.createTag(name, value, typeToken, intent)?:continue
            if (tag != null) break
        }
        intent.parents.pop()
        return tag
    }

    override fun <V : Any> toValue(tag: Tag<out Any>, typeToken: TypeToken<V>, intent: HierarchicalConverterCallerIntent): Pair<String?, V>? {
        for (delegate in delegateList) {
            return delegate.toValue(tag, typeToken, intent) ?: continue
        }
        return null
    }
}
复制成功

Converter代理类的方法则比Serdes代理类复杂。这是来自于Gson的查找方法,遍历一个列表,并交给第一个可处理的Converter处理

由此,通过以上实现我们可以完成对嵌套类型的序列化/反序列化应用,例如对Map的序列化/反序列化

代码块
JavaScript
自动换行
复制代码
// 创建一个包含所有proxy可处理的值的map,例如Map,List,Int,String,ByteArray,...
val map:Map<String,Any> = someLegalMap() 
// 一个可以处理以上所有值的Converter proxy
val converterProxy:DefaultConverterProxy = someConverterProxy()
val mapTypeConverter:TagConverter<Collection<*>,HierarchicalConverterCallerIntent>
  = MapTypeConverter(converterProxy)
val serdesProxy:DefaultSerdesProxy = someSerdesProxy()
val compTagSerdes:Serdes<Collection<*>, HierarchicalSerdesCallerIntent>
  = CompoundTagSerdes(serdesProxy)

val tag:Tag<Collection<*>> = mapTypeConverter.createTag("some map", map, object:TypeToken<Map<String, Any>())
val bits:ByteArray = compTagSerdes.serialize(tag)
val deserializedTag = compTagSerdes.deserialize(bits, 0)
assertTrue(tag, deserializedTag) //序列化->反序列化后的tag和原tag相等
val map2 = mapTypeConverter.toValue(deserializedTag, object:TypeToken<Map<String, Any>())
assertTrue(map, map2) // 最终的map和原map相等

复制成功

并且同样的,SerdesProxy和TagConverterProxy也应该能完成以上工作。事实上这两个代理应该可以完成目前所有实现的Converter和Serdes的工作,只要正确地把其他Converter和Serdes放进这两个代理中即可。

以上便是目前我关于如何实现嵌套类型Tag的序列化/反序列化功能的目前所有开发结果,下一篇我应该会聊聊开发过程数次被推翻重改的那些上层接口们(以及无数代码代码改动的心酸泪目史QwQ)