2010-12-09 58 views
8

J'ai expérimenté avec Scala et XML et j'ai trouvé une étrange différence de comportement entre une balise XML créée avec XML.load (ou loadString) et l'écriture littérale. Voici le code:Scala XML.loadString vs expression littérale

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

Et ici, la sortie:

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 

Les types sont les mêmes. Mais il n'y a pas d'égalité. L'ordre des attributs change. Ce n'est jamais le même que l'original. Les attributs de la littérature sont triés par ordre alphabétique (seul danger?).

Cela ne poserait aucun problème si les deux solutions ne se comportaient pas différemment lorsque j'essayais de les transformer. J'ai récupéré un code intressant de Daniel C. Sobral au How to change attribute on Scala XML Element et j'ai écrit ma propre règle afin de supprimer la première barre oblique de l'attribut "href". Le RuleTransformer fonctionne bien avec l'in_xml, mais n'a aucun effet sur from_str!

Malheureusement, la plupart de mes programmes doivent lire du XML via XML.load (...). Donc, je suis coincé. Est-ce que quelqu'un est au courant de ce sujet?

Meilleures salutations,

Henri

+1

Ceci est certainement un bug. Pas que cela aide ... –

+0

Les littéraux Scala XML ont connu des problèmes avec l'ordre des attributs. Vous pouvez voter pour http://lampsvn.epfl.ch/trac/scala/ticket/2735. (Je ne sais pas s'ils font vraiment attention aux votes, mais cela ne peut pas faire de mal.) – Steve

+0

Ils ne font pas attention aux votes, bien qu'ils fassent attention à l'activité (les gens qui s'abonnent, font des commentaires, etc.). Pourtant, ça ne fait pas mal. Personnellement, je m'abonne à tout bogue qui m'intéresse et je mets en avant tout ce qui me semble particulièrement important. –

Répondre

0

Certains autres tests: Peut-être, mon premier test d'égalité ne convient pas:

in_xml == from_str 

et si je teste:

in_xml.equals(in_xml) 

Je reçois aussi faux. Peut-être, je devrais utiliser une autre méthode d'essai (comme correspond, mais je ne trouve pas ce prédicat que je devrais utiliser comme second paramètre ...)

Cela dit, si je teste ce qui suit dans le REPL

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

je reçois vrai, même sans faire appel à la méthode equals ...

Retour à mon premier exemple: je défini une règle de réécriture

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

Il utilise les classes d'aide/méthodes défini par Daniel C. Sobral au How to change attribute on Scala XML Element. Si je demande:

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

Je reçois le résultat attendu avec in_xml, mais aucune modification avec from_str ...

1

D'après ce que je peux voir, in_xml et from_str ne sont pas égaux parce que l'ordre des attributs est différent. C'est malheureux et dû à la façon dont le XML est créé par le compilateur.Cela provoque les attributs d'être différents:

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

Vous pouvez voir voir que si vous remplacez les attributs de la comparaison fonctionnera:

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

Cela dit, je ne suis pas clair pourquoi cela causerait un comportement différent dans le code qui remplace l'attribut href. En fait, je soupçonne que quelque chose ne va pas dans la façon dont fonctionne la cartographie des attributs. Par exemple, si je remplace le in_str avec:

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

Il fonctionne très bien. Se pourrait-il que le code d'attribut de Daniel ne fonctionne que si l'attribut est dans la position de tête de MetaData?


Side note: à moins in_xml est null, equals et == reviendriez la même valeur. La version == vérifie si le premier opérande est null avant d'appeler equals.