       switch (lhs, rhs) {
       case (.attribute(let lhsQuotes), .attribute(let rhsQuotes)):
          return lhsQuotes == rhsQuotes
-      case (.comment, .comment),
-           (.text, .text),
-           (.cdata, .cdata):
+      case (.text, .text),
+           (.cdata, .cdata),
+           (.comment, .comment):
          return true
       default:
          return false

   }
}

public enum EscapableContent: Equatable {
   fileprivate typealias Replacement = (unescaped: String, escaped: String)
   
   public enum Quotes: Equatable {
      case single
      case double
   }
   
   // See: https://stackoverflow.com/questions/1091945/what-characters-do-i-need-to-escape-in-xml-documents
   case attribute(quotes: Quotes)
   case comment
   case text
   case cdata
   case processingInstruction
   
   // See: https://en.wikipedia.org/wiki/XML#Escaping
   fileprivate var replacements: [Replacement] {


public extension String {
    public init(xmlDocumentRoot root: Element, version: Version = Version(major: 1), encoding: DocumentEncoding = .utf8, options: SerializationOptions = []) {
      let versionAttribute = "version=" + options.quotes.quoted(attributeString: version.versionString())
      let encodingAttribute = "encoding=" + options.quotes.quoted(attributeString: encoding.attributeValue)
      self = "<?xml \(versionAttribute) \(encodingAttribute)?>"
         + options.lineSeparator
         + String(xml: root)
   }
   
   public init(xml: Element, options: SerializationOptions = []) {
      let attributes = xml.attributes.isEmpty ? "" : " " + xml.attributes.map {
         $0.key + "=" + options.quotes.quoted(attributeString: $0.value)
      }.joined(separator: " ")
      let start = "<\(xml.name)\(attributes)"
      let end = "</\(xml.name)>"
