我能想到的最好的方法是为任何具有默认 xmlns 属性的元素提供不同的命名空间前缀的样式表。由于我的文档有多个 xmlns="..." 属性,因此将递归地应用到树中。
如果它对其他人有用,这里是......虽然我的 XSL 技能主要基于谷歌:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:ms="urn:schemas-microsoft-com:xslt"
>
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="root_default_ns" select="namespace-uri(/*)" />
<xsl:template match="node()|text()">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="node() | text()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:param name="old_default_ns" select="$root_default_ns"/>
<xsl:param name="new_default_ns" />
<!-- match any element in the source default namespace -->
<xsl:if test="namespace-uri() = $old_default_ns">
<xsl:element name="ns_{$new_default_ns}:{local-name()}" namespace="{$old_default_ns}">
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="node() | text()">
<xsl:with-param name="old_default_ns" select="$old_default_ns" />
<xsl:with-param name="new_default_ns" select="$new_default_ns" />
</xsl:apply-templates>
</xsl:element>
</xsl:if>
<!-- match any element with a prefix qualified namespace -->
<xsl:if test="namespace-uri() != $old_default_ns and contains(name(), ':')">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="node() | text()">
<xsl:with-param name="old_default_ns" select="$old_default_ns" />
<xsl:with-param name="new_default_ns" select="$new_default_ns" />
</xsl:apply-templates>
</xsl:element>
</xsl:if>
<!-- match any element *without* a prefix qualified namespace -->
<xsl:if test="namespace-uri() != $old_default_ns and contains(name(), ':') = false">
<xsl:variable name="new_ns" select="count(ancestor::*)" />
<xsl:element name="ns_{$new_ns}:{name()}" namespace="{namespace-uri()}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" >
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="node() | text()">
<xsl:with-param name="old_default_ns" select="namespace-uri()" />
<xsl:with-param name="new_default_ns"><xsl:value-of select="$new_ns" /></xsl:with-param>
</xsl:apply-templates>
</xsl:element>
</xsl:if>
</xsl:template>
<!-- match root element only, and inject Silverlight namespace -->
<xsl:template match="/*">
<xsl:element name="ns_root:{local-name()}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" namespace="{$root_default_ns}">
<!-- inject the Silverligth ns -->
<xsl:variable name="dummy">
<Dummy/>
</xsl:variable>
<xsl:copy-of select="ms:node-set($dummy)/*/namespace::*"/>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="node() | text()">
<xsl:with-param name="old_default_ns" select="$root_default_ns" />
<xsl:with-param name="new_default_ns">root</xsl:with-param>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
不幸的是,在跳过这个圈之后,我得到了一个 XAML 字符串,它现在符合 XamlReader 期望的约定,但会产生一个 System.ExecutionEngineException (WPF XamlReader 仍然可以很好地处理字符串)