Scala XML.loadString против буквального выражения

Я экспериментировал со Scala и XML и обнаружил странную разницу в поведении между тегом XML, созданным с помощью XML.load (или loadString), и записью его как литерала. Вот код:

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

Типы одинаковые. Но нет равенства. Порядок атрибутов меняется. Он никогда не будет таким же, как оригинальный. Атрибуты литерала отсортированы по алфавиту (только опасность?).

Это не было бы проблемой, если бы оба решения не вели себя по-разному, когда я пытаюсь их преобразовать. Я взял интересный код от Дэниела С. Собрала в Как изменить атрибут на Scala XML Element и написал собственное правило, чтобы удалить первую косую черту атрибута "href". RuleTransformer хорошо работает с in_xml, но не влияет на from_str!

К сожалению, большинству моих программ приходится читать там XML через XML.load(...). Итак, я застрял. Кто-то знает об этой теме?

С наилучшими пожеланиями,

Анри


person Henri Bauer    schedule 09.12.2010    source источник


Ответы (2)


Из того, что я вижу, 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>"""

Это работает нормально. Может ли быть так, что код атрибута от Дэниела работает только в том случае, если атрибут находится в позиции заголовка MetaData?


Примечание: если in_xml не равно null, equals и == вернут одно и то же значение. Версия == проверяет, является ли первый операнд нулевым, перед вызовом equals.

person huynhjl    schedule 11.12.2010

Некоторые дополнительные тесты: возможно, мой первоначальный тест на равенство не подходит:

in_xml == from_str

и если я проверю:

in_xml.equals(in_xml)

Я также получаю ложь. Возможно, мне следует использовать другой метод тестирования (вроде соответствует, но я не нашел, какой предикат я должен использовать в качестве второго параметра...)

Тем не менее, если я проверю следующее в REPL

<body id="1234"></body> == XML.loadString("<body id=\"1234\"></body>")

Я получаю true, даже не вызывая метод 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
    }
}

Он использует вспомогательные классы/методы, определенные Дэниелом С. Собралом в How to изменить атрибут в XML-элементе Scala. Если я подам заявку:

new RuleTransformer(changeCSS).transform(in_xml)
new RuleTransformer(removeComments).transform(from_str)

Я получаю ожидаемый результат с in_xml, но без изменений с from_str...

person Henri Bauer    schedule 10.12.2010