0

属性名、現在の属性値、および属性に必要な新しい値を提供することで、ユーザーが XML 属性を変更できるようにする XML 解析ユーティリティを作成しようとしています。これが私のコードです:

def main (args: Array[String]) {
   val xml= <Rule debug="true" expression="testing"/>
   val printable= replaceXMLEntryAttribute(xml, "debug", "true", "false")
   println(printable)
}
/**
   * This method is used to iterate over the entirety of the xml presented and modify the XML attribute desired 
   */
  def replaceXMLEntryAttribute(elem: Elem, attrName: String, curVal: String, desiredVal: String): Elem = {

    def replace(current: Elem): Elem = current.copy(
      child = current.map {
        case e: Elem if isReplacementEntry(current, attrName, curVal) ⇒ generateReplacementXMLAttribute(current)
        case e: Elem ⇒ replace(e)
        case other⇒ other
      }
    )

   def generateReplacementXMLAttribute(node: Elem): Elem = {
      val currentXML= node.toString()
      val newAttr= currentXML.replace(curVal, desiredVal)
      return XML.loadString(newAttr)
    }
    replace(elem)
  }

  private def isReplacementEntry(node: Elem, attributeName: String,  currentAttrValue: String): Boolean = {
   val attr = "@" + attributeName
   val exists = node \\ attr find { _.text == currentAttrValue }
   exists match{
     case None => false
     case _ => true
   }

目的の出力は<Rule debug="false" expression="testing"/> 、プログラムの結果が<Rule debug="true" expression="testing"><Rule expression="testing" debug="false"/></Rule>

ここでは、replace メソッドが台無しになっているとしか言いようがありません。

4

2 に答える 2

2

メソッドのドキュメントには、そのElem.mapメソッドが何をするのかを説明するテキストがなく、その型が紛らわしいです。より具体的な型を取得するには、Scala インタープリターを使用できます。

scala> import scala.xml._
scala> val elem: Elem = <numbers><one/><two/><three/></numbers>
scala> :t elem.map(identity)
scala.xml.NodeSeq

奇妙なことに、なぜそれが生成されるのNodeSeqでしょうか? がElem.map要素の子をマッピングしElem、同じラベルと属性を持つが新しい子を持つ を返す場合、戻り値の型はElemではなくになりNodeSeqます。が本当にその子を反復しているかどうかを確認するためElem.mapに、遭遇したノードをリストに蓄積しましょう。

scala> var nodes = Seq[Node]()
scala> elem.map {node =>
     |   nodes :+= node
     |   node
     | }
res: scala.xml.NodeSeq = NodeSeq(<numbers><one/><two/><three/></numbers>)
scala> nodes
res: Seq[scala.xml.Node] = List(<numbers><one/><two/><three/></numbers>)

子に対して反復処理を行っていれば、 と予想List(<one/>, <two/>, <three/>)していましたが、そうではありませんでした。そのため、要素自体を含む 1 要素のコレクションを反復しているように見えますが、これはあまり役に立ちません。しかし、コードを見ると、これは意図的なものであるように思われます:Nodeは、拡張により、そのシーケンスが単一の要素自体で構成される のElemサブクラスです。NodeSeq

要約すると、予期しない結果が得られる理由は、 から開始し<Rule debug="true" expression="testing"/>、それをマップして結果<Rule debug="true" expression="testing"/>を取得し、 Rule の子をその結果に置き換えて、 を取得するため<Rule debug="true" expression="testing"><Rule expression="testing" debug="false"/></Rule>です。

問題のその部分の解決策は、current.child.map代わりに を使用することですcurrent.map。ただし、Rule 自体ではなく、Rule のゼロの子のみを調べているため、マップの本体は実行されないため、debug 属性は変更されません。パターン マッチングとマップを交換することをお勧めします。

def replace: Node => Node =
  {
    case e: Elem if isReplacementEntry(e, attrName, curVal) ⇒ generateReplacementXMLAttribute(e)
    case e: Elem ⇒ e.copy(
      child = e.child.map { replace(_) }
    )
    case other⇒ other
  }

Nodes ではなくs で動作するように型を修正した後Elem、目的の結果が得られます。

于 2015-09-04T01:54:26.743 に答える
0

Check my library Advxml on github to replace and edit xml documents!

https://github.com/geirolz/advxml

Example:

import com.github.geirolz.advxml.all._
import scala.xml._
import scala.util._

//import MonadError instance for Try
import cats.instances.try_._

val doc: Elem = 
<Persons>
  <Person Name="Mimmo">
    <Cars>
      <Car Brand="Fiat"/>
    </Cars>
  </Person>
</Persons>

val rule: XmlRule = $(_ \ "Person" \ "Cars")
    ==> Replace(<Cars><Car Brand="Lamborghini"/></Cars>)

val result: Try[NodeSeq] = doc.transform[Try](rule)  
于 2019-08-06T20:21:02.413 に答える