XSLT中的身份转换顾名思义:就是将一种元素或属性的格式替换为另一种新的格式。尤其当处理一些结构中比较灵活的XML标识时,格式转换就显得特别关键。就像新闻的文章结构,不管标题在文章的什么地方被提及,都会被突显为斜体。但是,在XML样式中并不用为文章指定斜体格式,而是指定表明标题类型的词语,这样不同的转换就可以用自己的方式来处理词语。当你想要将这样的XML文档转换成易读的格式(如HTML),你除了要完整地保留原文内容,还需要保持突显的词语周围的文本结构。就比如说:
<Paragraph sequenceID=”1”>This week a remastered version of the movie <title type=”movie”>Raider’s of the Lost Ark</title> was released.</Paragraph>
转换为:
<p>本周,重灌版电影 <i>夺宝奇兵</i>将公映。</p>
于是你在浏览器中将看到:
本周,重灌版电影夺宝奇兵将公映。
(注意:其实我也不知道夺宝奇兵这电影是否真的会在这周公映,我只是最近比较关注这事。)
让我们再来看看一个相对更完整的例子。在我们前一节使用过的AdventureWorks源文档中(你可以从秘籍一下载它的XML文档和相关的注释),有个<AdditionalContactInfo>元素,它包含了contact的phone numbers, shipping 和 billing addresses等信息。为了简单化,我们在这就只看<eMail>元素。<eMail>这个元素会显示在Contacts中所有<AdditionalContactInfo>元素出现的地方。这样,你就可以用在秘籍二中讨论过的XML Descendant axis 属性来查看到所有对<eMail>的引用。接着,你对这个查询结果反复使用替换方法,将每个<eMail>元素替代为一个新的身份。例如:
Dim emails = (From email In _
xmlDoc.<Contacts>.<Contact>.<aci:AdditionalContactInfo>...<act:eMail>).ToList()
For Each email In emails
TransformEmail(email)
Next
在这个例子里,输出的文档格式为HTML,其中的<eMail>元素被替代为一个固定的指向E-mail地址的发送邮件的链接元素。在上面的那段代码中可以看到它将<eMail> Xelement 作为参数传递给了这个做替换的TransformEmail函数。
Private Sub TransformEmail(ByVal email As XElement)
Dim emailHtml = <div class="Email">
<a href=<%= "mailto:" & email.<act:eMailAddress>.Value %>>
<%= email.<act:eMailAddress>.Value %>
</a>
</div>
email.ReplaceWith(emailHtml)
End Sub
下面我学以致用,来创建一个HTML文档。
Imports <xmlns="http://SampleSchema/AWContacts">
Imports <xmlns:aci="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactInfo">
Imports <xmlns:act="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes">
Public Class Recipe3
Public Function GetContactsHtml(ByVal xmlDoc As XDocument) As XElement
' 用发送邮件的链接来替换E-Mail地址
Dim emails = (From email In xmlDoc.<Contacts>.<Contact>.<aci:AdditionalContactInfo>...<act:eMail>).ToList()
For Each email In emails
TransformEmail(email)
Next
' 创建HTML文档
Return <html>
<body>
<table border="1">
<%= From contact In xmlDoc.<Contacts>.<Contact> _
Select <tr>
<td valign="top">
<%= contact.<FirstName>.Value & " " & contact.<LastName>.Value %>
</td>
<td valign="top">
<%= contact.<aci:AdditionalContactInfo> %>
</td>
</tr> _
%>
</table>
</body>
</html>
End Function
Private Sub TransformEmail(ByVal email As XElement)
Dim emailHtml = <div class="Email">
<a href=<%= "mailto:" & email.<act:eMailAddress>.Value %>>
<%= email.<act:eMailAddress>.Value %>
</a>
</div>
email.ReplaceWith(emailHtml)
End Sub
End Class
在XSLT中,相同的转换如下:
<?xmlversion='1.0'?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:aw="http://SampleSchema/AWContacts"
xmlns:aci="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactInfo"
xmlns:act="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes">
<xsl:outputmethod="html"indent="yes"/>
<xsl:templatematch="aw:Contacts">
<html>
<body>
<tableborder="1">
<xsl:apply-templatesselect="aw:Contact" />
</table>
</body>
</html>
</xsl:template>
<xsl:templatematch="aw:Contact">
<tr>
<tdvalign="top">
<xsl:value-ofselect="aw:FirstName"/>
<xsl:text> </xsl:text>
<xsl:value-ofselect="aw:LastName"/>
</td>
<tdvalign="top">
<xsl:apply-templatesselect="aci:AdditionalContactInfo" />
</td>
</tr>
</xsl:template>
<xsl:templatematch="aci:AdditionalContactInfo">
<divclass="AdditionalInfo">
<xsl:copy>
<xsl:apply-templatesselect="@* | node()" />
</xsl:copy>
</div>
</xsl:template>
<xsl:templatematch="act:eMail">
<divclass="Email">
<a>
<xsl:attributename="href">
<xsl:text>mailto:</xsl:text>
<xsl:value-ofselect="act:eMailAddress" />
</xsl:attribute>
<xsl:value-ofselect="act:eMailAddress" />
</a>
</div>
</xsl:template>
</xsl:stylesheet>
按元素类型分类:
在Visual Basic里它可以按照元素的类型将转换分类。例如,我们已经在上面的例子中使用过定义好了一个address、一个e-mail和一个phone number类型的ContactTypes.xsd文件。那个文件还能识别三种不同的address类型名和五种不同的phone number类型名。在XSLT中,你可以为每一个address类型创建一个模板,为了直观表示,三个不同的address名字以三种不同的并且能表达相关信息的模板结尾,保持一个原则:格式统一。在Visual Basic中,你可以很容易的创建一个关于所有不同的address 或phone number元素的查询,然后将其传到作为你XSLT模板的那个VB替换函数里。例如:
Dim addresses = (From addr In info...<act:homePostalAddress>).Union( _
From addr In info...<act:physicalDeliveryOfficeName>).Union( _
From addr In info...<act:registeredAddress>).ToList()
For Each address In addresses
TransformAddress(address)
Next
...
Private Sub TransformAddress(ByVal address As XElement)
Dim addressHtml = _
<div class="Address">
<%= address.<act:Street>.Value %><br/>
<%= address.<act:City>.Value & ", " %> 
<%= address.<act:StateProvince>.Value %> 
<%= address.<act:PostalCode>.Value %><br/>
<%= address.<act:CountryRegion>.Value %><br/>
<%= _
GetSpecialInstructions(address.<act:SpecialInstructions>.ToList()) %>
</div>
address.ReplaceWith(addressHtml)
End Sub
打印 | 张贴于 2008-10-28 14:15:10 | Tag:VB Team Blog LINQ Cookbook
留言反馈