我對使用 XSL/XSLT 執行 XML 轉換非常陌生,并且有一個我正在尋求幫助的場景。
TLDR;問題的總結。我正在研究一個 C# 解決方案來轉義 MathML 中的某些字符,特別是在<mtext>節點中。字符包括但不一定限于{、}、[和],它們需要分別更新為\{、\}、\[和\]。看到人們對 XSLT 轉換所做的一些有趣的事情,我想我會試一試。
作為參考,這里有一個 MathML 的示例塊:
<math style='font-family:Times New Roman' xmlns='http://www.w3.org/1998/Math/MathML'>
<mstyle mathsize='15px'>
<mrow>
<mtext>4 ___ {</mtext>
<mtext mathvariant='italic'>x</mtext>
<mtext>: </mtext>
<mtext mathvariant='italic'>x</mtext>
<mtext> is a natural number greater than 4}</mtext>
</mrow>
</mstyle>
</math>
擺弄了一下,我發現使用這個 XSL,我可以列印出每個<mtext>元素的內容:
<?xml version='1.0' encoding=""UTF-8""?>
<xsl:stylesheet version=""1.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"">
<xsl:output method=""xml"" indent=""yes""/>
<xsl:template match=""node()|@*"">
<xsl:copy>
<xsl:apply-templates select=""node()|@*"" />
</xsl:copy>
</xsl:template>
<xsl:template match=""/"">
<xsl:for-each select=""//*[local-name()='mtext']"">
<xsl:variable name=""myMTextVal"" select=""text()"" />
<xsl:message terminate=""no"">
<xsl:value-of select=""$myMTextVal""/>
</xsl:message>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
我的第一個想法(這似乎很快就會成為一條錯誤的道路)是translate()在 for-each 回圈中使用 a 作為 XSL 2.0 的 XSL 1.0 版本replace():
<!-- Outside of the looping template -->
<xsl:param name=""braceOpen"" select=""'{'"" />
<xsl:param name=""braceOpenReplace"" select=""'\{'"" />
<!-- In the loop itself -->
<xsl:value-of select=""translate(//*[local-name()='mtext']/text(), $braceOpen, $braceOpenReplace)""/>
當第一個 mtext 的內容開始顯示為“4 ___ \”而不是“4 ___ \{”時,使用 translate 對 1:1 替換變體的限制的問題很快變得明顯。
所以再挖掘一些,我遇到了這些執行緒:
XSLT 字串替換
未找到 XSLT 替換功能
兩者都提供了替代解決方案replace()。所以我設定了一個測驗:
<xsl:template name=""ProcessMathText"">
<xsl:param name=""text""/>
<xsl:param name=""replace""/>
<xsl:param name=""by""/>
<xsl:choose>
<xsl:when test=""contains($text,$replace)"">
<xsl:value-of select=""substring-before($text,$replace)""/>
<xsl:value-of select=""$by""/>
<xsl:call-template name=""ProcessMathText"">
<xsl:with-param name=""text"" select=""substring-after($text,$replace)""/>
<xsl:with-param name=""replace"" select=""$replace""/>
<xsl:with-param name=""by"" select=""$by""/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select=""$text""/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
并將其放置在for-each塊中:
<xsl:otherwise>
<xsl:variable name=""mTextText"" select=""Text"" />
<xsl:call-template name=""ProcessMathText"">
<xsl:with-param name=""text"" select=""$mTextText""/>
<xsl:with-param name=""replace"" select=""'{'""/>
<xsl:with-param name=""by"" select=""'\{'""/>
</xsl:call-template>
</xsl:otherwise>
但是,這開始拋出“'xsl:otherwise' 不能是 'xsl:for-each' 元素的子元素。” 錯誤。最終,我不能 100% 確定如何“呼叫”<xsl:otherwise>上面鏈接中所述的內容,而不是在for-each塊內,根據我在 AS、JS、Python 和C#,所以我希望有人能夠幫助我,或者為我指出一個可能產生結果的方向,而不是我只是把頭撞在墻上。
我在輸出中注意到的另一個可能問題...看起來轉換導致丟失 HTML 物體字符(例如 ),并將它們替換為“”,這是我不想要的,因為可能會導致一些煩人的頭痛。有沒有辦法維護結構,只替換特定內容,而不會意外替換或在某種意義上“呈現”HTML 物體?
在此先感謝您的幫助!
uj5u.com熱心網友回復:
很難理解您的問題實際上是什么。
為了在mtext節點中轉義“某些字符”,請考慮以下簡化示例:
XML
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle mathsize="15px">
<mrow>
<mtext>some text{with} all kinds of (brackets) in it</mtext>
<mtext>a different {example}</mtext>
<mtext>no change expected here</mtext>
<mtext>({tough one?})</mtext>
</mrow>
</mstyle>
</math>
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://www.w3.org/1998/Math/MathML">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="math:mtext">
<xsl:copy>
<xsl:call-template name="escape-chars">
<xsl:with-param name="string" select="."/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template name="escape-chars">
<xsl:param name="string"/>
<xsl:param name="chars">(){}</xsl:param>
<xsl:choose>
<xsl:when test="$chars">
<xsl:variable name="char" select="substring($chars, 1, 1)" />
<xsl:choose>
<xsl:when test="contains($string, $char)">
<!-- process substring-before with the remaining chars -->
<xsl:call-template name="escape-chars">
<xsl:with-param name="string" select="substring-before($string, $char)"/>
<xsl:with-param name="chars" select="substring($chars, 2)"/>
</xsl:call-template>
<!-- escape matched char -->
<xsl:value-of select="concat('\', $char)"/>
<!-- continue with substring-after -->
<xsl:call-template name="escape-chars">
<xsl:with-param name="string" select="substring-after($string, $char)"/>
<xsl:with-param name="chars" select="$chars"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- pass the entire string for processing with the remaining chars -->
<xsl:call-template name="escape-chars">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="chars" select="substring($chars, 2)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
結果
<?xml version="1.0" encoding="UTF-8"?>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle mathsize="15px">
<mrow>
<mtext>some text\{with\} all kinds of \(brackets\) in it</mtext>
<mtext>a different \{example\}</mtext>
<mtext>no change expected here</mtext>
<mtext>\(\{tough one?\}\)</mtext>
</mrow>
</mstyle>
</math>
uj5u.com熱心網友回復:
在 XSLT 3.0 中,它看起來像這樣:
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://www.w3.org/1998/Math/MathML">
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="math:mtext">
<xsl:copy>
<xsl:value-of select=". => replace('[', '\[', 'q')
=> replace(']', '\]', 'q')
=> replace('{', '\{', 'q')
=> replace('}', '\}', 'q')"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/495856.html
上一篇:如何隱藏依賴的Nuget包?
下一篇:VBA回圈和做while
