Search code examples
scalascala-xml

Scala XML literal - Boolean vs String


I ran into this behavior which surprised me. Essentially, if I create "the same" XML Elem from two different XML literals, they fail to equal one another. The unique thing here is that I am using a Boolean in one and a String in the other.

scala> import scala.xml._
import scala.xml._

scala> val t: Boolean = true
t: Boolean = true

scala> val fromBoolean: Elem = <b>{t}</b>
fromBoolean: scala.xml.Elem = <b>true</b>

scala> val fromString = <b>true</b>
fromString: scala.xml.Elem = <b>true</b>

scala> fromString == fromBoolean
res0: Boolean = false

Is this the expected behavior?

It seems that Scala is storing the underlying type and a Boolean doesn't strictly equal a String.

Is this correct interpretation, and can anyone explain what exactly is going on here? I couldn't find a way to inspect the underlying type within the two nodes. If I look at the children they just appear to be Nodes.

scala> fromString.child(0)
res1: scala.xml.Node = true

scala> fromBoolean.child(0)
res2: scala.xml.Node = true

Solution

  • Your interpretation is correct. The child of fromString is scala.xml.Text, which extends scala.xml.Atom[String]:

    scala> fromString.child(0).getClass.getName
    res1: String = scala.xml.Text
    
    scala> fromString.child(0).asInstanceOf[xml.Atom[_]].data.getClass.getName
    res2: String = java.lang.String
    

    And the child of fromBoolean is scala.xml.Atom[Boolean]:

    scala> fromBoolean.child(0).getClass.getName
    res3: String = scala.xml.Atom
    
    scala> fromBoolean.child(0).asInstanceOf[xml.Atom[_]].data.getClass.getName
    res4: String = java.lang.Boolean
    

    So the data of fromString's child Atom has type String, and the data of fromBoolean's Atom has type Boolean. The equality implementation of Atom (scala.xml.Atom#strict_==) just compares the data directly, and so the String and the Boolean compare unequal.

    I'm not sure what's the purpose of distinguishing the types of Atom data. It seems to me that Atom should compare toString values of its data anyway. So this behaviour might be a bug.

    As a workaround, I can advise to convert the atom values to String explicitly. The equality works in that case:

    scala> <b>{true.toString}</b> == <b>true</b>
    res5: Boolean = true
    

    Scala xml comparison does have more quirks though:

    scala> <foo:b xmlns:foo="foo"/> == <foo:b xmlns:foo="bar"/> 
    res6: Boolean = true
    
    scala> <b>{"<"}</b> == <b>&lt;</b>
    res7: Boolean = false
    
    scala> <b>></b> == <b>&gt;</b>
    res8: Boolean = false
    

    So it may be worth it to try implementing comparison manually.