我一直在尝试使用Scala和XML,我发现使用XML.load(或loadString)创建的XML标记与将其写为文字的行为之间存在奇怪的差异。这是代码:
import scala.xml._
// creating a classical link HTML tag
val in_xml = <link type="text/css" href="/css/main.css" rel="stylesheet" xmlns="http://www.w3.org/1999/xhtml"></link>
// The same as a String
val in_str = """<link type="text/css" href="/css/main.css" rel="stylesheet" xmlns="http://www.w3.org/1999/xhtml"></link>"""
// Convert the String into XML
val from_str = XML.loadString(in_str)
println("in_xml : " + in_xml)
println("from_str: "+ from_str)
println("val_xml == from_str: "+ (in_xml == from_str))
println("in_xml.getClass() == from_str.getClass(): " +
(in_xml.getClass() == from_str.getClass()))
在这里,输出:
in_xml : <link href="/css/main.css" rel="stylesheet" type="text/css" xmlns="http://www.w3.org/1999/xhtml"></link>
from_str: <link rel="stylesheet" href="/css/main.css" type="text/css" xmlns="http://www.w3.org/1999/xhtml"></link>
val_xml == from_str: false
in_xml.getClass() == from_str.getClass(): true
类型相同。但是没有平等。属性的顺序会发生变化。它永远不会与原来的相同。文字的属性按字母顺序排序(只有危险?)。
如果两种解决方案在尝试转换它们时行为不同,那么这不会成为问题。我在How to change attribute on Scala XML Element从Daniel C. Sobral那里获得了一些有趣的代码并编写了我自己的规则,以便删除“href”属性的第一个斜杠。 RuleTransformer适用于in_xml,但对from_str没有影响!
不幸的是,我的大部分程序都必须通过XML.load(...)读取XML。所以,我被困住了。有人知道这个话题吗?
致以最诚挚的问候,
亨利
答案 0 :(得分:1)
从我所看到的情况来看,in_xml
和from_str
不相等,因为属性的顺序不同。这很不幸,并且由于编译器创建XML的方式。这导致属性不同:
scala> in_xml.attributes == from_str.attributes
res30: Boolean = false
您可以看到,如果替换属性,则比较将起作用:
scala> in_xml.copy(attributes=from_str.attributes) == from_str
res32: Boolean = true
话虽如此,我不清楚为什么会在替换href
属性的代码中导致不同的行为。事实上,我怀疑属性映射的工作方式有问题。例如,如果我将in_str
替换为:
val in_str = """<link type="text/css" rel="stylesheet" href="/css/main.css"
xmlns="http://www.w3.org/1999/xhtml"></link>"""
工作正常。可能是Daniel的属性代码仅在属性位于MetaData
的头部位置时才有效吗?
附注:除非in_xml
为null
,否则equals
和==
将返回相同的值。 ==
版本将在调用equals
之前检查第一个操作数是否为空。
答案 1 :(得分:0)
进一步测试: 也许,我最初的平等测试不合适:
in_xml == from_str
如果我测试:
in_xml.equals(in_xml)
我也变得虚假。也许,我应该使用另一种测试方法(比如对应,但我没有找到我应该使用的谓词作为第二个参数...)
那就是说,如果我在REPL中测试以下内容
<body id="1234"></body> == XML.loadString("<body id=\"1234\"></body>")
即使没有调用equals方法,我也是如此......
回到我的初始示例:我定义了重写规则
def unSlash(s: String) = if (s.head == '/') s.tail else s
val changeCSS = new RewriteRule {
override def transform(n: Node): NodeSeq = n match {
case e: Elem if (n \ "@rel").text == "stylesheet" =>
e.copy(attributes = mapMetaData(e.attributes) {
case g @ GenAttr(_, key, Text(v), _) if key == "href" =>
g.copy(value = Text(unSlash(v)))
case other => other
})
case n => n
}
}
它使用Daniel C. Sobral在How to change attribute on Scala XML Element定义的辅助类/方法。如果我申请:
new RuleTransformer(changeCSS).transform(in_xml)
new RuleTransformer(removeComments).transform(from_str)
我使用in_xml获得了预期的结果,但没有使用from_str修改...