Scala:隐式地将对象渲染为复杂的XML?

时间:2015-03-14 18:28:44

标签: xml scala implicit-conversion

例如,我怎样才能(或为什么我可以)使用URL方法自动将所有implicit个对象转换为相关的超链接:

implicit def urlToXML(url: URL): xml.Node =
  <a href={ url.toString }>{ url.getHost + url.getPath }</a>

然后我可能(隐含地)使用它:

val myURL = new URL("https://example.com")
<p>Hey, come check out my cool new website at { myURL }.</p>

Scala(2.11.5)似乎忽略了implicit方法并将URL对象直接转换为字符串。

2 个答案:

答案 0 :(得分:0)

那是因为当使用大括号来转义XML文字时,需要String。如果找到对象,则调用toString。你可以在这里看到:

<p>{ new {} }</p> //returns <p>$anon$1@17aaa54f</p>

您可能以前只是在大括号中插入其他XML节点,因为toStringNode上运行良好,允许您执行以下操作:

<p>{ <div/> }</p> //returns <p><div/></p>

但这只是真的有效,因为<div/>.toString == "<div/>"。所以在你的情况下,编译器无法知道你想要将myUrl转换为Node,然后然后调用toString - 它认为它只需拨打toString上的URL

有很多方法可以解决这个问题。您可以使用类型归属来触发隐式转换:

<p>Hey, come check out my cool new website at { myURL:Node }.</p> 

您可以明确调用该方法(在此版本中,您不需要隐式转换):

<p>Hey, come check out my cool new website at { urlToXml(myURL) }.</p> 

或者制作一个需要Node

的包装器方法
def insertUrl(urlNode: Node) = <p>Hey, come check out my cool new website at { urlNode }.</p>
insertUrl(myUrl) //The implicit comes in to play here

答案 1 :(得分:0)

转化必须由预期类型或成员选择触发。在这种情况下,嵌入式Scala表达式的预期类型为Any

这是它的编译方式:

scala> <tag>{ 42 }{ new java.net.URI("http://acme.com") }</tag>
[[syntax trees at end of                     typer]] // <console>
package $line3 {
  object $read extends scala.AnyRef {
    def <init>(): $line3.$read.type = {
      $read.super.<init>();
      ()
    };
    object $iw extends scala.AnyRef {
      def <init>(): type = {
        $iw.super.<init>();
        ()
      };
      object $iw extends scala.AnyRef {
        def <init>(): type = {
          $iw.super.<init>();
          ()
        };
        private[this] val res0: scala.xml.Elem = {
          {
            new scala.xml.Elem(null, "tag", scala.xml.Null, scala.this.xml.TopScope, false, ({
              val $buf: scala.xml.NodeBuffer = new scala.xml.NodeBuffer();
              $buf.&+(42);
              $buf.&+(new java.net.URI("http://acme.com"));
              $buf
            }: _*))
          }
        };
        <stable> <accessor> def res0: scala.xml.Elem = $iw.this.res0
      }
    }
  }
}

文档为here for NodeBuffer.&+

另外,

scala> implicit class `uri anchor`(val u: java.net.URI) { def a = <a href={ u.toString }>{ u.getHost }</a> }
defined class uri$u0020anchor

scala> <tag>{ new java.net.URI("http://acme.com").a }</tag>
res1: scala.xml.Elem = <tag><a href="http://acme.com">acme.com</a></tag>

scala> 

另一种方法是提供一个自定义xml库,该库在该位置采用有用的类型,例如Embeddable,您可以为其提供所需的类型类。

另一种选择是预测Scala的未来并使用插值器。