没有在scala xml

时间:2015-09-09 13:55:45

标签: xml scala

我正在创建一个XML替换实用程序,它将替换XML文件中的属性。但由于某种原因,如果我试图替换的属性包含一个&符号,我写的布尔方法(isreplacementEntry)没有正确处理它。我的代码如下

  /**
   * This method is used to iterate over the entirety of the xml presented and modify the XML attribute desired while
   * keeping old XML values
   * @param elem the XML you pass in (generally from file, input stream or string)
   * @param attrName name of the attribute you want to replace
   * @param curVal value of the current attribute
   * @return
   */
  def replaceXMLEntryAttribute(elem: Elem, attrName: String, curVal: String, desiredVal: String): Node = {
    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
    }

    def generateReplacementXMLAttribute(node: Elem): Elem = {
      println("gets here")
      val currentXML= node.toString()
      val newAttr= currentXML.replaceAllLiterally("\""+curVal, "\""+desiredVal)

      XML.loadString(newAttr)

    }
    replace(elem)
  }

  /**
   * This method checks whether
   * @param node  The elements from which you wish to check if such an attribute exists
   * @param attributeName The name of the attribute to modify
   * @param currentAttrValue The current value of thr attribute that will eventually be modified
   * @return
   */
  private def isReplacementEntry(node: Elem, attributeName: String,  currentAttrValue: String): Boolean = {
  /*  val test = node.mkString)
    println(test)*/
    val attr = "@" + attributeName
    val exists = node \\ attr find { _.text == currentAttrValue }
    exists match{
      case None => false
      case _ => true
    }
  }

具体来说,此行val exists = node \\ attr find { _.text == currentAttrValue }始终返回false。我试过的东西是使用scala.xml.Utility.escape和当前的Attr Value,但它似乎仍然不起作用。

这个应该使用的示例XML是

<?xml version="1.0"?>
<Stuff expression="test">
<Rule expression="threshold &lt;= 20 AND '${userID}' IN ('USER1','USER2','USER_7','USER_09')"/>
</Stuff>

我试图用规则

替换该行

1 个答案:

答案 0 :(得分:1)

第一个问题可能是您正在寻找错误的字符串。 XML中的&符号表示字符引用的开头,当您调用elem.text时,它们将替换为相应的字符。尝试在Scala控制台中运行以下行:<foo attr="&lt;"/> \@ "attr"。它会生成字符串<

因此,对于给定的XML,参数应如下所示:

replaceXMLEntryAttribute(
  yourXml, 
  "expression", 
  "threshold <= 20 AND '${userID}' IN ('USER1','USER2','USER_7','USER_09')", 
  "some new value")

这仍然不够,因为你在XML的序列化字符串表示中字面替换了值, 那些放大器。因此,即使您在isReplacementEntry中找到了某些属性,也无法在generateReplacementXMLAttribute中替换它。相反,在元素上使用属性替换运算符%可能更容易,更安全:

def generateReplacementXMLAttribute(node: Elem): Elem = 
  node % xml.Attribute(attrName, xml.Text(desiredVal), xml.Null)

但问题是,这只会替换其参数的直接属性,但在isReplacementEntry中您与\\匹配,因此如果任意,它将返回true descendant元素具有所需值的属性。相反,只有当元素具有自己的具有此值的属性时,它才应为true

private def isReplacementEntry(node: Elem, attributeName: String,  currentAttrValue: String): Boolean = 
  node \@ attributeName == currentAttrValue

所以这里是代码的完整工作版本(没有Scaladoc)。请注意,您要搜索的值应该使用实际字符替换字符引用:

def replaceXMLEntryAttribute(elem: Elem, attrName: String, curVal: String, desiredVal: String): Node = {
  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
  }

  def generateReplacementXMLAttribute(node: Elem): Elem =
    node % xml.Attribute(attrName, xml.Text(desiredVal), xml.Null)

  replace(elem)
}

private def isReplacementEntry(node: Elem, attributeName: String,  currentAttrValue: String): Boolean =
  node \@ attributeName == currentAttrValue

println(replaceXMLEntryAttribute(
  <Stuff expression="test">
    <Rule expression="threshold &lt;= 20 AND '${userID}' IN ('USER1','USER2','USER_7','USER_09')"/>
  </Stuff>, 
  "expression", 
  "threshold <= 20 AND '${userID}' IN ('USER1','USER2','USER_7','USER_09')", 
  "foobar"))