Scala XML 属性替换导致将修改后的节点附加为子节点
Scala XML attribute replacement results in appending modified node as a child
我正在尝试编写一个 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>
我只能猜测并说替换方法在这里搞砸了。
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
映射到元素的 children,return 将具有相同标签和属性但新的 children 的 Elem
,return 类型应该是 Elem
,而不是 NodeSeq
。为了验证 Elem.map
是否真的在其 children 上迭代,让我们将遇到的节点累积到一个列表中。
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>)
如果我们迭代 children,我会期望 List(<one/>, <two/>, <three/>)
,但那不是我们得到的。所以看起来我们正在迭代一个包含元素本身的 1 元素集合,这不是很有用。然而,看看 the code,这似乎是有意为之:Node
,并且通过扩展 Elem
,是 NodeSeq
的子类,其序列由单个元素本身组成。
所以,总结一下,你得到意想不到的结果的原因是你从 <Rule debug="true" expression="testing"/>
开始,然后映射它以获得结果 <Rule debug="true" expression="testing"/>
,然后你替换 children 的规则,获得 <Rule debug="true" expression="testing"><Rule expression="testing" debug="false"/></Rule>
.
那部分问题的解决方案是使用 current.child.map
而不是 current.map
。但是,由于您只检查 Rule 的零 children 而不是 Rule 本身,映射的主体永远不会执行,因此 debug 属性保持不变。我建议交换 pattern-matching 和地图:
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
}
修复类型以使用 Node
s 而不是 Elem
s 后,我获得了想要的结果。
在 github 上查看我的图书馆 Advxml 以替换和编辑 xml 文档!
https://github.com/geirolz/advxml
示例:
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)
我正在尝试编写一个 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>
我只能猜测并说替换方法在这里搞砸了。
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
映射到元素的 children,return 将具有相同标签和属性但新的 children 的 Elem
,return 类型应该是 Elem
,而不是 NodeSeq
。为了验证 Elem.map
是否真的在其 children 上迭代,让我们将遇到的节点累积到一个列表中。
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>)
如果我们迭代 children,我会期望 List(<one/>, <two/>, <three/>)
,但那不是我们得到的。所以看起来我们正在迭代一个包含元素本身的 1 元素集合,这不是很有用。然而,看看 the code,这似乎是有意为之:Node
,并且通过扩展 Elem
,是 NodeSeq
的子类,其序列由单个元素本身组成。
所以,总结一下,你得到意想不到的结果的原因是你从 <Rule debug="true" expression="testing"/>
开始,然后映射它以获得结果 <Rule debug="true" expression="testing"/>
,然后你替换 children 的规则,获得 <Rule debug="true" expression="testing"><Rule expression="testing" debug="false"/></Rule>
.
那部分问题的解决方案是使用 current.child.map
而不是 current.map
。但是,由于您只检查 Rule 的零 children 而不是 Rule 本身,映射的主体永远不会执行,因此 debug 属性保持不变。我建议交换 pattern-matching 和地图:
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
}
修复类型以使用 Node
s 而不是 Elem
s 后,我获得了想要的结果。
在 github 上查看我的图书馆 Advxml 以替换和编辑 xml 文档!
https://github.com/geirolz/advxml
示例:
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)