1

我需要将以下输入 XML 转换为所需的输出 XML 格式。在这个论坛的帮助下,我得到了如下解决方案:

输入 XML

<?xml version="1.0"?>
<dataset  xmlns="http://developer.cognos.com/schemas/xmldata/1/"  xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
<metadata>
    <item name="Employee Id" />
    <item name="Employee Name" />
    <item name="Department Name" />
</metadata>
<data>
    <row>
      <value>1</value>
      <value Salutation="Dr." >John</value>
      <value>Finance</value>
    </row>
    <row>
      <value>2</value>
      <value Salutation="Mr." >Peter</value>
      <value>Admin</value>
    </row>
</data>
</dataset>

XSLT 转换

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:c="http://developer.cognos.com/schemas/xmldata/1/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vNames" select="/*/c:metadata/*/@name" />
<xsl:template match="/*/c:data">
    <dataset>
        <xsl:apply-templates/>
    </dataset>
</xsl:template>
<xsl:template match="c:row">
    <row>
        <xsl:apply-templates/>
    </row>
</xsl:template>
<xsl:template match="c:row/*">
    <xsl:variable name="vPos" select="position()"/>
    <xsl:element name="{translate($vNames[$vPos], ' ', '_')}">
        <xsl:apply-templates select="@*"/>
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>
<xsl:template match="@*">
    <xsl:attribute name="{name()}">
        <xsl:value-of select="." />
    </xsl:attribute>
</xsl:template>
</xsl:stylesheet>

所需的输出 XML

<?xml version="1.0" encoding="UTF-16"?>
<dataset xmlns:c="http://developer.cognos.com/schemas/xmldata/1/">
<row>
    <Employee_Id>1</Employee_Id>
    <Employee_Name Salutation="Dr.">John</Employee_Name>
    <Department_Name>Finance</Department_Name>
</row>
<row>
    <Employee_Id>2</Employee_Id>
    <Employee_Name Salutation="Mr.">Peter</Employee_Name>
    <Department_Name>Admin</Department_Name>
</row>

但是,我遇到了一个打破这个解决方案的特殊情况。输入 XML 中的属性值可以以数字、特殊字符或空格开头。

新输入 XML

<?xml version="1.0"?>
<dataset  xmlns="http://developer.cognos.com/schemas/xmldata/1/"  xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
<metadata>
    <item name="1Employee Id" />
    <item name=" Employee Name" />
    <item name="$Department Name" />
</metadata>
<data>
<row>
    <value>1</value>
    <value Salutation="Dr." >John</value>
    <value>Finance</value>
</row>
<row>
    <value>2</value>
    <value Salutation="Mr." >Peter</value>
    <value>Admin</value>
</row>
</data>
</dataset>

由于名称属性值被转换为元素名称,因此上述转换失败,因为元素名称不能以数字或空格开头。在这种情况下,我想用元素名称的一些有效字符替换这些字符,_或者C_获得相同的所需输出 XML。

我该如何处理这种情况?

4

2 回答 2

1

这很容易。只需扩展您的 translate() 调用范围以涵盖问题字符(您在属性中使用的字符,但 XML 禁止元素名称)

例如,改变...

<xsl:element name="{translate($vNames[$vPos], ' ', '_')}">

...到...

<xsl:element name="{translate($vNames[$vPos], ' 1$', '___')}">

更新

为了回应OP的澄清,这里是如何只翻译第一个字符。假设我们的第一个不安全字符的暂定元素名称是 $vName。为了说明,假设我们只是$将其视为唯一可能的不安全的第一个字符。我们所做的是去掉第一个字符,翻译它,然后重新添加它,就像这样......

<xsl:element name="{concat( translate( substring( $vName, 1, 1), '$', '_'),
                            substring( $vName, 2))}" />

...或者...

<xsl:element name="{translate( substring( $vName, 1, 1), '$', '_')}{substring( $vName, 2)}" />

笔记

如果您能够升级到 XSLT 2.0,那么使用正则表达式会变得更容易......

<xsl:element name="{replace( $vName, '^[$]', '_')}" />
于 2012-10-03T08:08:13.793 回答
1

这是一个不使用任何硬编码特殊字符的完整解决方案

<xsl:stylesheet version="1.0"
 xmlns:c="http://developer.cognos.com/schemas/xmldata/1/"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:strip-space elements="*"/>

    <xsl:variable name="vNames" select="/*/c:metadata/*/@name" />

    <xsl:variable name="vAlpha" select=
    "concat('ABCDEFGHIJKLMNOPQRSTUVWXYZ',
            'abcdefghijklmnopqrstuvwxyz')"/>

    <xsl:template match="/*/c:data">
        <dataset>
            <xsl:apply-templates/>
        </dataset>
    </xsl:template>
    <xsl:template match="c:row">
        <row>
            <xsl:apply-templates/>
        </row>
    </xsl:template>

    <xsl:template match="c:row/*">
        <xsl:variable name="vPos" select="position()"/>
        <xsl:variable name="vChar1" select=
         "translate(substring($vNames[$vPos],1,1),
                    translate(substring($vNames[$vPos],1,1), $vAlpha, ''),
                    '_')"/>
        <xsl:element name=
               "{$vChar1}{translate(substring($vNames[$vPos],2), ' ', '_')}">
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="@*">
        <xsl:attribute name="{name()}">
            <xsl:value-of select="." />
        </xsl:attribute>
    </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时:

<dataset  xmlns="http://developer.cognos.com/schemas/xmldata/1/"  xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
    <metadata>
        <item name="1Employee Id" />
        <item name=" Employee Name" />
        <item name="$Department Name" />
    </metadata>
    <data>
        <row>
            <value>1</value>
            <value Salutation="Dr." >John</value>
            <value>Finance</value>
        </row>
        <row>
            <value>2</value>
            <value Salutation="Mr." >Peter</value>
            <value>Admin</value>
        </row>
    </data>
</dataset>

产生了想要的正确结果

<dataset xmlns:c="http://developer.cognos.com/schemas/xmldata/1/">
   <row>
      <_Employee_Id>1</_Employee_Id>
      <_Employee_Name Salutation="Dr.">John</_Employee_Name>
      <_Department_Name>Finance</_Department_Name>
   </row>
   <row>
      <_Employee_Id>2</_Employee_Id>
      <_Employee_Name Salutation="Mr.">Peter</_Employee_Name>
      <_Department_Name>Admin</_Department_Name>
   </row>
</dataset>

说明

  1. 我们可以通过使用双重翻译方法来识别不属于给定字符集的所有字符,该方法首先由 Michael Kay 提出。

  2. 如果名称除了空格之外还有其他非法字符,则可以使用相同的双重翻译技术将任何此类(预先未知的)字符替换为所需的合法字符。

于 2012-10-03T12:42:43.827 回答