xml - 使用 Scala 更改 XML 命名空间

标签 xml scala

我正在使用 scala 通过 scala.xml.XML.loadFile() 方法从文件加载 XML 文件。我正在使用的文档已经定义了 namespace ,我希望使用 scala 将 namespace 更改为其他名称。例如,文档的 xmlns 为“http://foo.com/a”,前缀为“a”- 我想将文档的命名空间和前缀分别更改为“http://foo.com/b”和“b”。

看起来很简单,我觉得我在这里遗漏了一些明显的东西。从引用的 loadFile() 方法返回的 Elem 获取命名空间没有问题。

最佳答案

在这里。由于 NamespaceBinding 是嵌套的(每个 ns 都有一个父级,TopScope 除外),我们需要递归来修复它。此外,每个 ns 都有一个 URI 和一个前缀,我们需要更改两者。

下面的函数将只更改一个特定的 URI 和前缀,它会检查所有命名空间,以查看前缀或 URI 是否需要更改。它将更改彼此独立的前缀或 URI,这可能不是我们想要的。不过,解决这个问题没什么大不了的。

至于其余部分,只需对 Elem 进行模式匹配以递归到 XML 的每个部分。啊,是的,它也改变了元素的前缀。同样,如果这不是我们想要的,也很容易改变。

代码假设没有必要递归到 XML 的“其他”部分——其余部分通常是文本元素。此外,它假定其他地方没有 namespace 。我不是 XML 方面的专家,所以我在这两个方面都可能是错误的。再说一次,改变它应该很容易——只需遵循模式即可。

def changeNS(el: Elem, 
             oldURI: String, newURI: String, 
             oldPrefix: String, newPrefix: String): Elem = {
  def replace(what: String, before: String, after: String): String =
    if (what == before) after else what

  def fixScope(ns: NamespaceBinding): NamespaceBinding =
    if(ns == TopScope)
      TopScope
    else new NamespaceBinding(replace(ns.prefix, oldPrefix, newPrefix),
                              replace(ns.uri, oldURI, newURI),
                              fixScope(ns.parent))

  def fixSeq(ns: Seq[Node]): Seq[Node] = for(node <- ns) yield node match {
    case Elem(prefix, label, attribs, scope, children @ _*) => 
      Elem(replace(prefix, oldPrefix, newPrefix), 
           label, 
           attribs, 
           fixScope(scope), 
           fixSeq(children) : _*)
    case other => other
  }
  fixSeq(el.theSeq)(0).asInstanceOf[Elem]
}

但这会产生意想不到的结果。范围被添加到所有元素。那是因为 NamespaceBinding 没有定义 equals 方法,因此使用引用相等。我已经给它开票了,2138 , 已经关闭,所以 Scala 2.8 不会有这个问题。

同时,以下代码将正常工作。它保留命名空间缓存。它还在处理之前将 NamespaceBinding 分解为一个列表。

  def changeNS(el: Elem, 
             oldURI: String, newURI: String, 
             oldPrefix: String, newPrefix: String): Elem = {
  val namespaces = scala.collection.mutable.Map.empty[List[(String, String)],NamespaceBinding]

  def replace(what: String, before: String, after: String): String =
    if (what == before) after else what

  def unfoldNS(ns: NamespaceBinding): List[(String, String)] = ns match {
    case TopScope => Nil
    case _ => (ns.prefix, ns.uri) :: unfoldNS(ns.parent)
    }

  def foldNS(unfoldedNS: List[(String, String)]): NamespaceBinding = unfoldedNS match {
    case knownNS if namespaces.isDefinedAt(knownNS) => namespaces(knownNS)
    case (prefix, uri) :: tail =>
      val newNS = new NamespaceBinding(prefix, uri, foldNS(tail))
      namespaces(unfoldedNS) = newNS
      newNS
    case Nil => TopScope
  }

  def fixScope(ns: NamespaceBinding): NamespaceBinding =
    if(ns == TopScope)
      ns
    else {
      val unfoldedNS = unfoldNS(ns)
      val fixedNS = for((prefix, uri) <- unfoldedNS) 
                    yield (replace(prefix, oldPrefix, newPrefix), replace(uri, oldURI, newURI))

      if(!namespaces.isDefinedAt(unfoldedNS))
        namespaces(unfoldedNS) = ns  // Save for future use

      if(fixedNS == unfoldedNS)
        ns
      else 
        foldNS(fixedNS)
    }

  def fixSeq(ns: Seq[Node]): Seq[Node] = for(node <- ns) yield node match {
    case Elem(prefix, label, attribs, scope, children @ _*) => 
      Elem(replace(prefix, oldPrefix, newPrefix), 
           label, 
           attribs, 
           fixScope(scope), 
           fixSeq(children) : _*)
    case other => other
  }
  fixSeq(el.theSeq)(0).asInstanceOf[Elem]
}

关于xml - 使用 Scala 更改 XML 命名空间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1094600/

相关文章:

Scala 找到更优雅的方式

java - 带有 Spark 的 NoClassDefFoundError

java - 使用 MainActivity 中的布局在抽屉导航中显示登录用户

xml - Ionic 3 项目没有 config.xml

asp.net - 在 XML 文档中存储等号 (=)

scala - 将无形可扩展记录传递给函数(续)

c# - 如何优化XML的书写方式?

swift - 为什么我不能使用 Swift 4 加载 Collada 文件?

用于 Scala 的 SQLite

scala - 如何在 Scala 中处理空输入参数