You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scala3解析XML时PCData的变化及CDATA保留方案咨询

Scala3 XML解析CDATA的兼容问题及解决方法

问题背景

使用2.3.0版本XML解析器时发现Scala2与Scala3的差异:Scala2会将XML的CDATA结构转换为PCData对象,但Scala3中没有该类型,导致原Scala2里匹配PCData的代码完全失效,无法提取CDATA内容并保留原编码信息,最终无法让XML输出与原内容完全一致。

Scala2中的可行实现

以下是Scala2中成功提取CDATA并保证渲染一致性的核心逻辑:

def extract(node: Node): Try[CharSequence] =
  node match {
    case x: xml.Text =>
      Success(x.data)
    case CDATA(x) =>
      Success(x)
    case _ =>
      ... default behavior ...
  }
case class CDATA(content: String, pre: String, post: String) extends CharSequence
object CDATA { 
  def unapply(node: Node): Option[CDATA] = {
    node.child match {
      case Seq(pre: Node, PCData(x), post: Node) =>
        Some(CDATA(x, pre.text, post.text))
      case Seq(PCData(x)) =>
        Some(CDATA(x))
      case x =>
        None
    }
  }
}

CDATA类及伴生对象用于解析时识别CDATA结构,确保渲染XML时输出与原内容一致。

Scala2中的单元测试示例

it should "parse BalloonStyle" in {
val xml: Elem = <xml>
  <Style id="noDrivingDirections">
    <BalloonStyle>
      <text>
        <![CDATA[
      <b>$[name]</b>
      <br /><br />
      $[description]
    ]]>
      </text>
    </BalloonStyle>
  </Style>
</xml>

extractAll[Seq[StyleSelector]](xml) match {
  case Success(ss) =>
    ss.size shouldBe 1
    val style: StyleSelector = ss.head
    println(style)
    style match {
      case Style(styles) =>
        styles.size shouldBe 1
        styles.head match {
          case b@BalloonStyle(text, _, _, _) =>
            val expectedText =
              """
                |          <b>$[name]</b>
                |          <br /><br />
                |          $[description]
                |        """.stripMargin
            text.$.asInstanceOf[CDATA].content shouldBe expectedText
            val wy = TryUsing(StateR())(sr => Renderer.render(b, FormatXML(), sr))
            wy.isSuccess shouldBe true
            val expectedBalloonStyle =
              """<BalloonStyle>
                |  <text>
                |<![CDATA[
                |          <b>$[name]</b>
                |          <br /><br />
                |          $[description]
                |        ]]>
                |</text>
                |</BalloonStyle>""".stripMargin
              wy.get shouldBe expectedBalloonStyle
        }
      case _ => fail(s"wrong sort of StyleSelector: $style")
    }
  case Failure(x) => fail(x)
}
}

Scala3的兼容解决方案

Scala3的XML库中,CDATA不再封装为PCData类型,而是作为标签为#PCDATA的Elem节点处理,可通过以下方式修改解析逻辑:

修改后的CDATA提取逻辑

import scala.xml.*

def extract(node: Node): Try[CharSequence] =
  node match {
    case x: Text =>
      Success(x.data)
    case CDATA(x) =>
      Success(x)
    case _ =>
      // 原有默认逻辑
      Failure(new IllegalArgumentException("Unsupported node type"))
  }

case class CDATA(content: String, pre: String = "", post: String = "") extends CharSequence {
  override def length(): Int = content.length
  override def charAt(index: Int): Char = content.charAt(index)
  override def subSequence(start: Int, end: Int): CharSequence = content.subSequence(start, end)
  override def toString: String = content
}

object CDATA {
  def unapply(node: Node): Option[CDATA] = node match {
    case elem: Elem if elem.label == "#PCDATA" =>
      Some(CDATA(elem.text))
    case _ =>
      node.child match {
        case Seq(pre: Text, elem: Elem, post: Text) if elem.label == "#PCDATA" =>
          Some(CDATA(elem.text, pre.text, post.text))
        case Seq(elem: Elem) if elem.label == "#PCDATA" =>
          Some(CDATA(elem.text))
        case _ => None
      }
  }
}

关键说明

  • Scala3将CDATA解析为标签为#PCDATA的Elem节点,需通过标签判断识别
  • 修改CDATA.unapply方法适配新的节点结构,保留原类的接口以兼容原有渲染逻辑
  • 无需大幅修改单元测试,替换解析实现即可保证输出与原XML完全一致

内容的提问来源于stack exchange,提问作者Phasmid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 20:46:14