0

我想紧紧抓住微软员工后脑勺上的头发,用它作为杠杆,用力反复敲打他的头在坚硬的表面上!这会让我感觉几乎和现在解决这个问题一样好。

我有一个简单的 XML 消息,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<message>
    <cmd id="instrument_status">
        <status_id>1</status_id>
    </cmd>
</message>

我正在使用的设备上的 Web 服务返回了几条这样的消息,我正在将它们转换为不同的格式。对于上述消息,新格式如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<grf:message xmlns:grf="http://www.company.com/schemas/device/version001">
    <grf:messageHeader>
        <grf:messageType>instrumentStatus</grf:messageType>
    </grf:messageHeader>
    <grf:messageBody>
        <grf:instrumentStatusBody>
            <grf:statusId>Running</grf:statusId>
        </grf:instrumentStatusBody>
    </grf:messageBody>
</grf:message>

XML 中有一个 status_id 整数值的映射,如下所示:

status-id    Meaning  
=========    =======
0            Ready  
1            Running  
2            NotReady  
3            PoweringUp  
4            PoweringDown  
5            PoweredUp  
6            PoweredDown  
7            Tuning  
8            Error  

当我使用 Altova XMLSpy 时,我的 XSLT 工作正常并为我提供正确的输出,但是当我运行我的 .NET 应用程序时,我在将 status_id 整数的映射转换为允许的枚举值之一时遇到了故障字符串。MS XSLT 处理器没有获取枚举值,而是返回一个空字符串,并且我在输出 XML 中得到一个空的 <status_id/> 元素。

以下是我的 XSLT 代码,删除了一些部分以减少空间量:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:fo="http://www.w3.org/1999/XSL/Format"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:fn="http://www.w3.org/2005/xpath-functions"
        xmlns:msxsl="urn:schemas-microsoft-com:xslt"
        xmlns:grf="http://www.company.com/schemas/device/version001"
        exclude-result-prefixes="#default">

 <xsl:template match="/">
  <xsl:apply-templates select="message"/>
 </xsl:template>

 <xsl:template match="message">
  <xsl:element name="grf:message">
   <xsl:apply-templates select="/message/cmd/@id"/>
  </xsl:element>
 </xsl:template>

 <xsl:template match="/message/cmd/@id">
  <xsl:variable name="_commandType" select="/message/cmd/@id"/>
        <!-- Following line works in Altova XMLSpy, but fails in .NET app. ??? -->
  <xsl:variable name="_statusIdValue" select="/message/cmd/status_id"/>
  <xsl:element name="grf:messageHeader">
   <xsl:element name="grf:messageType">
    <xsl:choose>
     <xsl:when test="$_commandType = 'api_info'">
      <xsl:text>apiInfo</xsl:text>
     </xsl:when>
     <xsl:when test="$_commandType = 'instrument_status'">
      <xsl:text>instrumentStatus</xsl:text>
     </xsl:when>
    </xsl:choose>
   </xsl:element>
  </xsl:element>
  <xsl:element name="grf:messageBody">
   <xsl:choose>
    <xsl:when test="$_commandType = 'api_info'">
     <xsl:element name="grf:apiInfoBody">
      <xsl:element name="grf:apiVersion">
       <xsl:value-of select="/message/cmd/api-version"/>
      </xsl:element>
      <xsl:element name="grf:apiBuild">
       <xsl:value-of select="/message/cmd/api-build"/>
      </xsl:element>
     </xsl:element>
    </xsl:when>
    <xsl:when test="$_commandType = 'instrument_status'">
     <xsl:element name="grf:instrumentStatusBody">
      <xsl:element name="grf:statusId">
       <xsl:choose>
        <xsl:when test="$_statusIdValue = '0'">
         <xsl:text>Ready</xsl:text>
        </xsl:when>
        <xsl:when test="$_statusIdValue = '1'">
         <xsl:text>Running</xsl:text>
        </xsl:when>
        <xsl:when test="$_statusIdValue = '2'">
         <xsl:text>NotReady</xsl:text>
        </xsl:when>
        <xsl:when test="$_statusIdValue = '3'">
         <xsl:text>PoweringUp</xsl:text>
        </xsl:when>
        <xsl:when test="$_statusIdValue = '4'">
         <xsl:text>PoweringDown</xsl:text>
        </xsl:when>
        <xsl:when test="$_statusIdValue = '5'">
         <xsl:text>PoweredUp</xsl:text>
        </xsl:when>
        <xsl:when test="$_statusIdValue = '6'">
         <xsl:text>PoweredDown</xsl:text>
        </xsl:when>
        <xsl:when test="$_statusIdValue = '7'">
         <xsl:text>Tuning</xsl:text>
        </xsl:when>
        <xsl:when test="$_statusIdValue = '8'">
         <xsl:text>Error</xsl:text>
        </xsl:when>
       </xsl:choose>
      </xsl:element>
     </xsl:element>
    </xsl:when>
   </xsl:choose>
  </xsl:element>
 </xsl:template>
</xsl:stylesheet>

是否存在在 Altova XMLSpy 和 MS XSLT 处理器中行为相同的 XSLT 1.0 代码?

谢谢,

报警器

4

5 回答 5

2

需要注意的一点是,在匹配“消息”元素的模板中,您可以这样做

<xsl:apply-templates select="/message/cmd/@id"/> 

这实际上将尝试匹配 XML 中相对于文档根的第一条消息,而不管您当前使用的是什么消息。它没有选择相对于当前节点。在您的情况下,看起来只会有一条消息,因此在这里不会有问题,但在其他情况下会出现问题。

匹配元素而不是属性也可能更常见,尤其是在您想要处理元素的子元素的地方。所以,你可能会用这个代替上面的行

<xsl:apply-templates select="cmd"/> 

然后,对于匹配它的模板,而不是当前执行此操作

<xsl:template match="/message/cmd/@id"> 

你会这样做

<xsl:template match="cmd"> 

接下来,在此模板中,您可以尝试用更简单的 select 语句替换变量

<xsl:variable name="_commandType" select="@id"/> 
<xsl:variable name="_statusIdValue" select="status_id"/> 

看看这是否有所作为。

于 2010-01-09T16:23:17.297 回答
1

转型过于复杂了。试试这个相当简单的样式表:

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns="http://www.company.com/schemas/device/version001"
>
  <xsl:output indent="yes" encoding="utf-8" />

  <!-- main template / entry point -->
  <xsl:template match="message">
    <message>
      <messageHeader>
        <xsl:apply-templates select="cmd" mode="head" />
      </messageHeader>
      <messageBody>
        <xsl:apply-templates select="cmd" mode="body" />
      </messageBody>
    </message>
  </xsl:template>

  <!-- header templates -->
  <xsl:template match="cmd[@id = 'api_info']" mode="head">
    <messageType>apiInfo</messageType>
  </xsl:template>

  <xsl:template match="cmd[@id = 'instrument_status']" mode="head">
    <messageType>instrumentStatus</messageType>
  </xsl:template>

  <!-- body templates -->
  <xsl:template match="cmd[@id = 'api_info']" mode="body">
    <apiInfoBody>
      <apiVersion><xsl:value-of select="api-version" /></apiVersion>
      <apiBuild><xsl:value-of select="api-build" /></apiBuild>
    </apiInfoBody>
  </xsl:template>

  <xsl:template match="cmd[@id = 'instrument_status']" mode="body">
    <instrumentStatusBody>
      <statusId>
        <xsl:choose>
          <xsl:when test="status_id = 0">Ready</xsl:when>
          <xsl:when test="status_id = 1">Running</xsl:when>
          <xsl:when test="status_id = 2">NotReady</xsl:when>
          <xsl:when test="status_id = 3">PoweringUp</xsl:when>
          <xsl:when test="status_id = 4">PoweringDown</xsl:when>
          <xsl:when test="status_id = 5">PoweredUp</xsl:when>
          <xsl:when test="status_id = 6">PoweredDown</xsl:when>
          <xsl:when test="status_id = 7">Tuning</xsl:when>
          <xsl:when test="status_id = 8">Error</xsl:when>
          <!-- just in case… -->
          <xsl:otherwise>
            <xsl:text>Unknown status_id: </xsl:text>
            <xsl:value-of select="status_id" />
          </xsl:otherwise>
        </xsl:choose>
      </statusId>
    </instrumentStatusBody>
  </xsl:template>

</xsl:stylesheet>

我摆脱了所有看似多余的命名空间定义(根据需要添加它们)并将您的样式表放入默认命名空间。这意味着您不再需要'grf:'在每个元素上都添加前缀,而无需更改实际结果:

<?xml version="1.0" encoding="utf-8"?>
<message xmlns="http://www.company.com/schemas/device/version001">
  <messageHeader>
    <messageType>instrumentStatus</messageType>
  </messageHeader>
  <messageBody>
    <instrumentStatusBody>
      <statusId>Running</statusId>
    </instrumentStatusBody>
  </messageBody>
</message>

请注意我如何使用不同的匹配表达式和不同的模板模式在正​​确的情况下输出适当的元素。这样任何<xsl:variable><xsl:choose>变得不必要,从而使样式表更清洁且更易于维护。

此外,通常不需要<xsl:element>显式定义,除非您想输出具有动态名称的元素。在所有其他情况下,您可以直接编写元素。

很抱歉,我不能确定为什么您的样式表没有按预期运行。它对我有用,而且看起来还可以(ish)。

不要犹豫,询问以上是否有任何不清楚的地方。

于 2010-01-11T09:46:17.837 回答
0

自从我编写任何 xslt 代码以来已经有很长时间了,但根据我所看到的,您可能可以更改这一行:

<xsl:variable name="_statusIdValue" select="/message/cmd/status_id"/>

<xsl:variable name="_statusIdValue" select="/message/cmd/status_id/."/>

这应该告诉它选择元素的内容与节点本身。

有点像当您执行 value-of 操作并且您想要节点的文本内容时。你会做同样的事情。

例如,如果您想吐出状态 ID 号,您可以使用以下内容:

<xsl:value-of select="/message/cmd/status_id/."/>
于 2010-01-08T19:12:41.363 回答
0

这是如此糟糕的编码,我真的很高兴它在 .NET 中不起作用,我建议你重写你的样式表。

试试这个:

<xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:grf="http://www.company.com/schemas/device/version001">

<xsl:template match="message">
   <xsl:variable name="commmand-type" select="cmd/@id"/>
   <xsl:variable name="status-id" select="cmd/status_id/text()"/>

      <grf:message>
         <grf:messageHeader>
            <grf:messageType>
               <xsl:choose> 
                  <xsl:when test="$commmand-type = 'api_info'">apiInfo</xsl:when> 
                  <xsl:when test="$commmand-type = 'instrument_status'">instrumentStatus</xsl:when> 
               </xsl:choose> 
            </grf:messageType>
         </grf:messageHeader>

         <grf:messageBody>
            <xsl:choose> 
               <xsl:when test="$commmand-type = 'api_info'"> 
                  <grf:apiInfoBody>
                     <grf:apiVersion>
                        <xsl:value-of select="cmd/api-version"/>
                     </grf:apiVersion>
                  </grf:apiInfoBody>
                  <grf:apiBuild> 
                     <xsl:value-of select="cmd/api-build"/> 
                  </grf:apiBuild> 
               </xsl:when> 
               <xsl:when test="$commmand-type = 'instrument_status'"> 
                  <grf:instrumentStatusBody>
                     <grf:statusId>
                        <xsl:choose> 
                           <xsl:when test="$status-id = '0'">Ready</xsl:when> 
                           <xsl:when test="$status-id = '1'">Running</xsl:when> 
                           <xsl:when test="$status-id = '2'">NotReady</xsl:when> 
                           <xsl:when test="$status-id = '3'">PoweringUp</xsl:when> 
                           <xsl:when test="$status-id = '4'">PoweringDown</xsl:when> 
                           <xsl:when test="$status-id = '5'">PoweredUp</xsl:when> 
                           <xsl:when test="$status-id = '6'">PoweredDown</xsl:when> 
                           <xsl:when test="$status-id = '7'">Tuning</xsl:when> 
                           <xsl:when test="$status-id = '8'">Error</xsl:when> 
                        </xsl:choose>
                     </grf:statusId>
                  </grf:instrumentStatusBody>
               </xsl:when> 
            </xsl:choose>
        </grf:messageBody>
    </grf:message>
</xsl:template>

</xsl:stylesheet>

我只用了一个<xsl:template>,如果你觉得合适可以重构。

于 2010-01-08T19:36:29.927 回答
0

好的,我发现如果我使用以下行来分配 _statusIdValue 变量,那么代码将与 XslCompiledTransform 类一起正常运行:

<xsl:variable name="_statusIdValue" select="msxsl:node-set(/message/cmd/*)/text()"/>

这替换了原来的行:

<xsl:variable name="_statusIdValue" select="/message/cmd/status_id"/>

但是,适用于 XslCompiledTransform 类的分配不适用于 Altova XMLSpy。是否存在可以在 Altova XMLSpy 编辑器和 XslCompiledTransform 类中正常工作的分配变体?

谢谢,

报警器

于 2010-01-08T22:56:00.360 回答