scala.xml.节点报告未访问

pgx2nnw8  于 2023-03-08  发布在  Scala
关注(0)|答案(1)|浏览(136)

我目前正在使用Scala 3处理大的XML文件。我希望每个节点(XML元素)在没有读取所有属性和子节点时都能自动报告。我有一个解决方案,但感觉它不是很优雅,甚至可能有点滥用scala.util.Using的原始用途。

object ReportingNode:
  implicit val releasable: Releasable[ReportingNode] = node =>
    node.assumeNoOtherAttributes()
    node.assumeNoOtherChildren()

class ReportingNode(node: Node):
  private val description = (node.label + " " + node \@ "OID").trim
  private var accessedAttributes = Set[String]()
  private var accessedChildren = Set[String]()

  @targetName("attribute")
  def \@(attributeName: String): String =
    accessedAttributes += attributeName
    node \@ attributeName

  @targetName("children")
  def \(childName: String): NodeSeq =
    accessedChildren += childName
    node \ childName

  private def assumeNoOtherAttributes(): Unit =
    for (attribute <- node.attributes)
      if !accessedAttributes.contains(attribute.key) then
        println(s"WARN $description has unexpected attribute ${attribute.key}")

  private def assumeNoOtherChildren(): Unit =
    for (child <- node.child)
      if !(accessedChildren + "#PCDATA").contains(child.label) then
        println(s"WARN $description has unexpected child ${child.label}")

类似<foo oid="42" name="bob"><bar...的用法:

case class Foo(context: String, name: String, bars: Seq[Bar])

object Foo:
  def apply(n: Node): Foo = Using.resource(ReportingNode(n))(node =>
    val oid = node \@ "oid"
    val name = node \@ "name"
    val bars = (node \ "bar").map(Bar.apply)
    Foo(oid, name, bars)
  )

有没有办法把这样的事情做好?或者这已经是它得到的最好的了?
一些背景:你可能会问为什么我首先要把节点转换成一个自定义的结构。好吧,这里我简化了一点。XML是相当复杂的(CDISC ODM加上扩展),我的代码是解析对定义的引用,等等。

ovfsdjhp

ovfsdjhp1#

好吧,至少现在用法看起来干净了:

object Foo:
  def apply(n: Node): Foo = ReportingNode(n)(node =>
    val oid = node \@ "oid"
    val name = node \@ "name"
    val bars = (node \ "bar").map(Bar.apply)
    Foo(oid, name, bars)
  )

我通过将Using的内容移动到ReportingNode对象中的apply def来实现这一点:

object ReportingNode:
  implicit val releasable: Releasable[ReportingNode] = node =>
    node.assumeNoOtherAttributes()
    node.assumeNoOtherChildren()
  def apply[A](node: Node)(job: ReportingNode => A): A =
    Using.resource[ReportingNode, A](new ReportingNode(node))(job)

类别保持不变。

相关问题