2010-12-09 23 views
8

He estado experimentando con Scala y XML y encontré una extraña diferencia en el comportamiento entre una etiqueta XML creada con XML.load (o loadString) y escribiéndola como un literal. Aquí está el código:Scala XML.loadString vs expresión literal

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())) 

Y aquí, la salida:

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 

Los tipos son los mismos. Pero no hay igualdad. El orden de los atributos cambia. Nunca es el mismo que el original. Los atributos del litteral están ordenados alfabéticamente (¿solo riesgo?).

Esto no sería un problema si ambas soluciones no se comportaron de manera diferente cuando intento transformarlas. Recogí un código interesante de Daniel C. Sobral al How to change attribute on Scala XML Element y escribí mi propia regla para eliminar la primera barra del atributo "href". ¡RuleTransformer funciona bien con in_xml, pero no tiene efecto en from_str!

Desafortunadamente, la mayoría de mis programas tienen que leer XML a través de XML.load (...). Entonces, estoy atascado. ¿Alguien sabe sobre este tema?

Saludos,

Henri

+1

Esto es definitivamente un error. No es que eso ayude ... –

+0

Los literales de Scala XML tienen problemas conocidos con el orden de los atributos. Puede votar por http://lampsvn.epfl.ch/trac/scala/ticket/2735. (No sé si realmente prestan atención a los votos, pero no puede hacer daño.) – Steve

+0

No prestan atención a los votos, aunque prestan atención a la actividad (personas que se suscriben, hacen comentarios, etc.). Aún así, no duele. Personalmente, me suscribo a cualquier error que me interese y recomiendo cualquiera que considero particularmente importante. –

Respuesta

0

Algunas pruebas adicionales: Tal vez, mi prueba inicial de la igualdad no es apropiada:

in_xml == from_str 

y si lo prueban:

in_xml.equals(in_xml) 

También consigo obtener falso. Tal vez, debería usar otro método de prueba (como corresponde, pero no me di averiguar lo que un predicado que debe utilizar como segundo parámetro ...)

Dicho esto, si puedo probar lo siguiente en el REPL

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

consigo el verdadero, sin llamar al método equals ...

Volver a mi ejemplo inicial: he definido una regla de reescritura

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 
    } 
} 

utiliza las clases de ayuda/métodos definido por Daniel C. Sobral al How to change attribute on Scala XML Element. Si aplico:

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

consigo el resultado esperado con in_xml, pero sin modificación con from_str ...

1

Por lo que puedo ver, in_xml y from_str son no es igual porque el orden de los atributos es diferente. Esto es desafortunado y debido a la forma en que el compilador crea el XML.Eso hace que los atributos de ser diferente:

scala> in_xml.attributes == from_str.attributes 
res30: Boolean = false 

se puede ver ver que si reemplaza los atributos de la comparación funcionará:

scala> in_xml.copy(attributes=from_str.attributes) == from_str 
res32: Boolean = true 

Dicho esto, no me queda claro por qué eso sería causar un comportamiento diferente en el código que reemplaza el atributo href. De hecho, sospecho que algo está mal con la forma en que funciona el mapeo de atributos. Por ejemplo, si se sustituye la in_str con:

val in_str = """<link type="text/css" rel="stylesheet" href="/css/main.css" 
xmlns="http://www.w3.org/1999/xhtml"></link>""" 

funciona bien. ¿Podría ser que el código de atributo de Daniel solo funciona si el atributo está en la posición de la cabeza de MetaData?


Nota al margen: a menos in_xml es null, equals y == devolvería el mismo valor. La versión == comprobará si el primer operando es nulo antes de llamar al equals.