Bài viết này sẽ hướng dẫn bạn cách sử dụng XSLT (eXtensible Stylesheet Language Transformations) để xử lý một chuỗi các thẻ XML liên tiếp, được phân tách bằng dấu phẩy, và chuyển đổi chúng thành định dạng (superscript) một cách hiệu quả. Việc này đặc biệt hữu ích khi bạn muốn tạo các chú thích hoặc tham chiếu trong tài liệu XML của mình. Mục tiêu là tạo ra nội dung có cấu trúc, dễ đọc và **tối ưu hóa cho SEO**.
xref
Thành sup
Giả sử bạn có một đoạn XML như sau, trong đó các thẻ xref
được phân tách bằng dấu phẩy:
<p>
Some text here
<xref id="1">1</xref>,
<xref id="2">2</xref>,
<xref id="3">3</xref>.
</p>
Chúng ta muốn chuyển đổi nó thành:
<p>
Some text here
<sup>1,2,3</sup>.
</p>
Hoặc, một định dạng "xấu" hơn nhưng vẫn chấp nhận được:
<p>
Some text here
<sup>1</sup><sup>,</sup><sup>2</sup><sup>,</sup><sup>3</sup>.
</p>
Chúng ta đã có template để chuyển đổi một thẻ xref
đơn lẻ thành sup
:
<xsl:template match="xref">
<sup>
<xsl:apply-templates/>
</sup>
</xsl:template>
Thách thức là làm thế nào để **khớp (match)** một nhóm các node được phân tách bằng dấu phẩy.
Một giải pháp hiệu quả trong XSLT 1.0 là sử dụng một "fined-grained" identity rule, kết hợp với các template cụ thể để xử lý nhóm các thẻ xref
. Dưới đây là một ví dụ:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()[1]|@*"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match="xref[not(preceding-sibling::node()[1]
[self::text() and starts-with(.,',')]
)
]">
<xsl:variable name="vBreakText" select="following-sibling::text()[not(starts-with(.,','))][1]"/>
<xsl:variable name="vPrecedingTheBreak" select="$vBreakText/preceding-sibling::node()"/>
<xsl:variable name="vFollowing" select=".|following-sibling::node()"/>
<xsl:variable name="vGroup" select="$vFollowing[count(.|$vPrecedingTheBreak)
=
count($vPrecedingTheBreak)
]
"/>
<sup>
<xsl:apply-templates select="$vGroup" mode="group"/>
</sup>
<xsl:apply-templates select="$vBreakText"/>
</xsl:template>
<xsl:template match="text()" mode="group">
<xsl:value-of select="normalize-space()"/>
</xsl:template>
</xsl:stylesheet>
**Giải thích:**
Khi áp dụng cho XML sau (một ví dụ phức tạp hơn):
<p>
Some text here
<xref id="1">1</xref>,
<xref id="2">2</xref>,
<xref id="3">3</xref>.
<ttt/>
<xref id="4">4</xref>,
<xref id="5">5</xref>,
<xref id="6">6</xref>.
<zzz/>
</p>
Kết quả sẽ là:
<p>
Some text here
<sup>1,2,3</sup>.
<ttt/>
<sup>4,5,6</sup>.
<zzz/>
</p>
sup
Cho Từng Dấu Phẩy
Nếu bạn chấp nhận định dạng có các thẻ sup
riêng cho từng dấu phẩy, giải pháp sẽ đơn giản hơn:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p/text()[normalize-space() = ',' and preceding-sibling::node()[1][self::xref]]">
<sup>,</sup>
</xsl:template>
<xsl:template match="xref">
<sup>
<xsl:apply-templates/>
</sup>
</xsl:template>
</xsl:stylesheet>
**Lưu ý:** Giải pháp này có thể không lý tưởng về mặt thẩm mỹ, nhưng nó đơn giản và dễ hiểu hơn.
XSLT 2.0 cung cấp khả năng sử dụng regular expression, giúp giải quyết vấn đề này một cách thanh lịch hơn:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:variable name="comma-regex">^\s*,\s*$</xsl:variable>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xref[preceding-sibling::node()[1]/
self::text()[matches(., $comma-regex)]/
preceding-sibling::*[1]/self::xref]"/>
<xsl:template match="text()[matches(., $comma-regex) and
preceding-sibling::*[1]/self::xref and following-sibling::*[1]/self::xref]"/>
<xsl:template match="xref">
<sup>
<xsl:call-template name="process-xref-series">
<xsl:with-param name="next" select="."/>
</xsl:call-template>
</sup>
</xsl:template>
<xsl:template name="process-xref-series">
<xsl:param name="next"/>
<xsl:if test="$next">
<xsl:value-of select="$next"/>
<xsl:variable name="followingXref"
select="$next/following-sibling::node()[1]/
self::text()[matches(., $comma-regex)]/
following-sibling::*[1]/self::xref"/>
<xsl:if test="$followingXref">
<xsl:text>,</xsl:text>
<xsl:call-template name="process-xref-series">
<xsl:with-param name="next" select="$followingXref"/>
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
**Lưu ý:** Giải pháp này yêu cầu XSLT 2.0 để sử dụng hàm `matches()` với regular expression. Nếu bạn thay `matches(., $comma-regex)` bằng `normalize-space(.) = ','`, bạn có thể sử dụng XSLT 1.0.
Bài viết này đã trình bày nhiều giải pháp để xử lý chuỗi thẻ XML liên tiếp và chuyển đổi chúng thành định dạng superscript bằng XSLT. Việc lựa chọn giải pháp phù hợp phụ thuộc vào yêu cầu cụ thể của bạn, bao gồm phiên bản XSLT bạn đang sử dụng, yêu cầu về định dạng và mức độ phức tạp của stylesheet. Bằng cách sử dụng các kỹ thuật này, bạn có thể tạo ra các tài liệu XML có cấu trúc tốt, dễ đọc và **tối ưu hóa cho công cụ tìm kiếm**.
Bài viết liên quan