在两个仅名称空间不同的模式之间转换XML文档

本文关键字:模式 之间 文档 XML 转换 空间 两个 | 更新日期: 2023-09-27 18:28:21

如果我对问题的描述不清楚或过于复杂,我会提前道歉。我只是想确保我把问题的所有方面都包括在内。

我有一个场景,我接收到对模式有效的XML文档,让我们称之为S1,看起来像这样(简化):

<?xml version="1.0" encoding="utf-8"?>
    <xs:schema
        targetNamespace="http://somename.org/original"
        xmlns="http://somename.org/original"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:imported="http://somename.org/originalimported"
        elementFormDefault="unqualified">
    <xs:import namespace="http://somename.org/originalimported"/>
    <xs:element name="someElement">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="imported:someelement" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

正如您所看到的,它导入了另一个命名空间,它看起来像这样(也简化了),并引用了其中的一个元素:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
    targetNamespace="http://somename.org/originalimported"
    xmlns="http://somename.org/originalimported"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    version="1.0"
    elementFormDefault="unqualified">
    <xs:element name="someelement">
        <xs:complexType>
            ...
        </xs:complexType>
    </xs:element>
</xs:schema>

我还有另外两个模式"镜像"了上面两个模式,其中唯一的区别是命名空间"http://somename.org/original"替换为"http://somename.org/new"和命名空间"http://somename.org/originalimported"替换为"http://somename.org/newimported".除此之外,完全相同。看起来像这样(让我们称之为S2):

<?xml version="1.0" encoding="utf-8"?>
<xs:schema
        targetNamespace="http://somename.org/new"
        xmlns="http://somename.org/new"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:imported="http://somename.org/newimported"
        elementFormDefault="unqualified">
    <xs:import namespace="http://somename.org/newimported"/>
    <xs:element name="someElement">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="imported:someelement" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

进口的

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
    targetNamespace="http://somename.org/newimported"
    xmlns="http://somename.org/newimported"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    version="1.0"
    elementFormDefault="unqualified">
    <xs:element name="someelement">
        <xs:complexType>
            ...
        </xs:complexType>
    </xs:element>
</xs:schema>

我需要做的是转换我收到的针对S1进行验证的任何文档,并将其转换为针对S2进行验证。做这件事最可靠、最快的方法是什么?我喜欢的一种方法是简单地在XML文档中使用字符串替换来替换实际的名称空间,但如果文档很大,这似乎不是最有效的方法。

实际的转换必须使用C#中可用的方法(包括XML/schema/XSLT类)来完成。

提前感谢!

在两个仅名称空间不同的模式之间转换XML文档

您可以使用类似的方法

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0"
  xmlns:input1="http://example.com/original"
  xmlns:input2="http://example.com/originalimported"
  exclude-result-prefixes="input1 input2">
<xsl:template match="@* | node()">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>
<xsl:template match="input1:*">
  <xsl:element name="{name()}" namespace="http://example.com/new">
    <xsl:apply-templates select="@* | node()"/>
  </xsl:element>
</xsl:template>
<xsl:template match="input2:*">
  <xsl:element name="{name()}" namespace="http://example.com/newimported">
    <xsl:apply-templates select="@* | node()"/>
  </xsl:element>
</xsl:template>
</xsl:stylesheet>

但我想看看一些示例文档来拼写和测试它。特别是elementFormDefault="unqualified"可能意味着里面的其他元素不在命名空间中,上面的元素会将它们复制到范围中父元素的命名空间中,这可能不是你想要的,所以也许可以做

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0"
  xmlns:input1="http://example.com/original"
  xmlns:input2="http://example.com/originalimported"
  exclude-result-prefixes="input1 input2">
<xsl:template match="@* | node()">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>
<xsl:template match="*">
  <xsl:element name="{name()}" namespace="{namespace-uri()}">
    <xsl:apply-templates select="@* | node()"/>
  </xsl:element>
</xsl:template>
<xsl:template match="input1:*">
  <xsl:element name="{name()}" namespace="http://example.com/new">
    <xsl:apply-templates select="@* | node()"/>
  </xsl:element>
</xsl:template>
<xsl:template match="input2:*">
  <xsl:element name="{name()}" namespace="http://example.com/newimported">
    <xsl:apply-templates select="@* | node()"/>
  </xsl:element>
</xsl:template>
</xsl:stylesheet>

更好。

至于提供名称空间作为参数,我将建议以下方法:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">
<xsl:param name="input-ns1" select="'http://example.com/original'"/>
<xsl:param name="output-ns1" select="'http://example.com/new'"/>
<xsl:param name="input-ns2" select="'http://example.com/originalimported'"/>
<xsl:param name="output-ns2" select="'http://example.com/newimported'"/>
<xsl:template match="@* | node()">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>
<xsl:template match="*">
  <xsl:element name="{name()}" namespace="{namespace-uri()}">
    <xsl:apply-templates select="@* | node()"/>
  </xsl:element>
</xsl:template>
<xsl:template match="*[namespace-uri() = $input-ns1]">
  <xsl:element name="{name()}" namespace="{$output-ns1}">
    <xsl:apply-templates select="@* | node()"/>
  </xsl:element>
</xsl:template>
<xsl:template match="*[namespace-uri() = $input-ns2]">
  <xsl:element name="{name()}" namespace="{$output-ns2}">
    <xsl:apply-templates select="@* | node()"/>
  </xsl:element>
</xsl:template>
</xsl:stylesheet>

但我忘记了在XSLT1.0中,匹配模式不允许使用变量引用,因此只有当您使用像Saxon 9或XmlPrime这样的XSLT2.0处理器时,这种方法才会起作用。