使用Scala更改XML名称空间

使用Scala更改XML名称空间,xml,scala,Xml,Scala,我使用scala通过scala.XML.XML.loadFile()方法从文件中加载XML文件。我正在处理的文档已经定义了名称空间,我希望使用scala将名称空间更改为其他名称空间。例如,文档的xmlns为“”,前缀为“a”-我想将文档的名称空间和前缀分别更改为“”和“b” 看起来很简单,我觉得我错过了一些明显的东西。从引用的loadFile()方法的返回Elem中获取名称空间没有问题。在这里。由于NamespaceBinding是嵌套的(每个ns都有一个父节点,TopScope除外),所以我们

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


看起来很简单,我觉得我错过了一些明显的东西。从引用的
loadFile()
方法的返回
Elem
中获取名称空间没有问题。

在这里。由于NamespaceBinding是嵌套的(每个ns都有一个父节点,TopScope除外),所以我们需要递归来解决这个问题。另外,每个ns都有一个URI和一个前缀,我们需要同时更改这两个

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

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

代码假定不需要递归到XML的“其他”部分——其余部分通常是文本元素。此外,它假定其他地方没有名称空间。我不是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]
}
def更换(el:Elem,
oldURI:String,newURI:String,
oldPrefix:String,newPrefix:String):元素={
def replace(what:String、before:String、after:String):String=
如果(what==before)后面还有什么
def fixScope(ns:NamespaceBinding):NamespaceBinding=
如果(ns==顶视)
顶镜
else新名称空间绑定(替换(ns.prefix、oldPrefix、newPrefix),
替换(ns.uri、oldURI、newURI),
fixScope(ns.parent))
def fixSeq(ns:Seq[Node]):Seq[Node]=for(Node)
元素(替换(前缀、旧前缀、新前缀),
标签,
attribs,
固定范围(范围),
fixSeq(儿童):*)
案例其他=>其他
}
修正顺序(el.theSeq)(0).作为[Elem]的替代
}
但是,这会产生一个意外的结果。作用域将添加到所有元素中。这是因为NamespaceBinding没有定义equals方法,因此使用了引用相等。我为它打开了一个票证,该票证已经关闭,因此Scala 2.8不会有此问题

同时,以下代码将正常工作。它保留名称空间缓存。它还将名称空间绑定分解为一个列表,然后再进行处理

  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]
}
def更换(el:Elem,
oldURI:String,newURI:String,
oldPrefix:String,newPrefix:String):元素={
val namespaces=scala.collection.mutable.Map.empty[List[(String,String)],NamespaceBinding]
def replace(what:String、before:String、after:String):String=
如果(what==before)后面还有什么
def unfolns(ns:NamespaceBinding):列表[(字符串,字符串)]=ns匹配{
箱顶镜=>零
大小写\=>(ns.prefix,ns.uri)::展开ns(ns.parent)
}
def foldNS(unfoldns:List[(String,String)]):NamespaceBinding=unfoldns匹配{
如果namespaces.isDefinedAt(knownNS)=>名称空间(knownNS),则案例knownNS
大小写(前缀,uri)::tail=>
val newNS=新名称空间绑定(前缀、uri、foldNS(尾部))
名称空间(展开的名称)=新名称
纽恩斯
无案例=>顶镜
}
def fixScope(ns:NamespaceBinding):NamespaceBinding=
如果(ns==顶视)
ns
否则{
val Unfolderns=Unfolderns(ns)
val fixedNS=用于((前缀,uri)其他
}
修正顺序(el.theSeq)(0).作为[Elem]的替代
}

此处存在小错误。属性也可以有限定名称。您还需要检查这些名称