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
相关产品推荐
相关产品推荐

