如何使用基于外部参数的XSLT转换XML?

时间:2015-10-23 16:11:30

标签: c# xml xslt

我有传入的XML消息,每条消息都有不同的架构。我想使用C#.Net作为我的XSLT处理器将该请求转换为另一个模式。以下是我所处情况的简化情景。

传入请求:

<?xml version="1.0" encoding="utf-8"?>
<Request xmlns="http://www.example.com/api">
  <SourceId>SourceId1</SourceId>
  <RequestId>RequestId1</RequestId>
  <StatusEvent>
    <TenderId>TenderId1</TenderId>
    <EventCode>TENDER_STARTED</EventCode>
  </StatusEvent>
</Request>

转换为:

<?xml version="1.0" encoding="utf-8"?>
<TransactionStatus xmlns="http://www.example1.com/api">
  <RequestId>RequestId1</RequestId>
  <TransactionId>TenderId1</TransactionId>
  <Event>TRANSACTION_STARTED</Event>
</TransactionStatus>

传入请求:

<?xml version="1.0" encoding="utf-8"?>
<Response xmlns="http://www.example.com/api">
  <SourceId>SourceId1</SourceId>
  <RequestId>RequestId1</RequestId>
   <TenderCreated>
    <TenderId>TenderId1</TenderId>
  </TenderCreated>
 </Response>

转换为:

 <?xml version="1.0" encoding="utf-8"?>
<TransactionStarted xmlns="http://www.example1.com/api">
  <RequestId>RequestId1</RequestId>
  <TransactionId>TenderId1</TransactionId>
 </TransactionStarted>

这是我目前用来实现上述结果的XSLT,

     <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ns0="http://www.example.com/api"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="ns0 xs">
  <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
  <xsl:template match="text()"/>
  <xsl:template match="ns0:StatusEvent[1]">
    <TransactionStatus
        xmlns="http://www.example1.com/api">
      <RequestId>
        <xsl:value-of select="//ns0:RequestId"/>
      </RequestId>
      <TransactionId>
        <xsl:value-of select="ns0:TenderId"/>
      </TransactionId>
      <Event>
        <xsl:value-of select="ns0:EventCode"/>
      </Event>
    </TransactionStatus>
  </xsl:template>

  <xsl:template match="ns0:TenderCreated[1]">
    <TransactionStarted
        xmlns="http://www.example1.com/api">
      <RequestId>
        <xsl:value-of select="//ns0:RequestId"/>
      </RequestId>
      <TransactionId>
        <xsl:value-of select="ns0:TenderId"/>
      </TransactionId>
     </TransactionStarted>
  </xsl:template>
   </xsl:stylesheet>

所以这是我的两个问题,

  1. 对于目前的情况,我得到了正确的结果,但有没有更好的方法来实现这一目标?
  2. 对于某些传入请求,我想根据外部参数选择模板,我该如何实现?
  3. 更新:关于第二个问题的更多说明,

    例如:在第二个传入请求中,我可能会TenderUpdated而不是TenderCreated,为此我想将其转换为TransactionUpdatedTransactionCanceled取决于外部字符串参数

    所以如果传入请求是,

    <?xml version="1.0" encoding="utf-8"?>
    <Response xmlns="http://www.example.com/api">
      <SourceId>SourceId1</SourceId>
      <RequestId>RequestId1</RequestId>
       <TenderUpdated>
        <TenderId>TenderId1</TenderId>
      </TenderUpdated>
     </Response>
    

    传递的参数为Update,翻译为

    <?xml version="1.0" encoding="utf-8"?>
        <TransactionUpdated xmlns="http://www.example1.com/api">
          <RequestId>RequestId1</RequestId>
          <TransactionId>TenderId1</TransactionId>
          <Update/>
         </TransactionUpdated>
    

    如果传递的参数为Cancel,则翻译为

       <?xml version="1.0" encoding="utf-8"?>
            <TransactionCanceled xmlns="http://www.example1.com/api">
              <RequestId>RequestId1</RequestId>
              <TransactionId>TenderId1</TransactionId>
              <Cancel/>
             </TransactionCanceled>
    

    这是简化的方案,实际消息包含更多xml标记,TransactionUpdatedTransactionCanceled差异很大。

1 个答案:

答案 0 :(得分:1)

如果您知道所有结果元素都应该在命名空间http://www.example1.com/api中,那么您可以将其放在xsl:stylesheet上。例如

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://www.example1.com/api"
    xmlns:ns0="http://www.example.com/api"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="ns0 xs">

对于参数,将其声明为

<xsl:param name="transactionName" select="'Updated'"/>

当您想使用该参数创建元素时,请不要使用文字结果元素,而是使用xsl:element

<xsl:element name="Transaction{$transactionName}">...</xsl:element>

不幸的是,在XSLT 1.0中,不允许在模式中使用参数或变量引用,因此要处理条件,您只能编写与元素名称匹配的模板,然后您需要使用xsl:choose/xsl:when来处理不同的元素名称。以下是您可以扩展的示例:

<xsl:transform
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0"
    xmlns:api="http://www.example.com/api"
    xmlns="http://www.example1.com/api"
    exclude-result-prefixes="api">

    <xsl:param name="transactionName" select="'Update'"/>

    <xsl:output indent="yes"/>

    <xsl:template match="api:Response">
        <xsl:element name="Transaction{$transactionName}">
            <xsl:apply-templates select="api:RequestId | api:TenderUpdated/api:TenderId"/>
            <xsl:choose>
                <xsl:when test="$transactionName = 'Update'">
                    <Update/>
                </xsl:when>
                <xsl:when test="$transactionName = 'Cancel'">
                    <Cancel/>
                </xsl:when>
            </xsl:choose>
        </xsl:element>
    </xsl:template>

    <xsl:template match="api:RequestId">
        <RequestId>
            <xsl:apply-templates/>
        </RequestId>
    </xsl:template>

    <xsl:template match="api:TenderId">
        <TransactionId>
            <xsl:apply-templates/>
        </TransactionId>
    </xsl:template>
</xsl:transform>

http://xsltransform.net/94rmq5R在线。

如果输入格式之间存在很多差异,那么我可能会试图通过不同的样式表来处理它们。如果那是不可能的话,那么在模板中为根分支并在模板上使用模式来区分处理可能是有意义的。例如。

    <xsl:template match="api:Response">
            <xsl:choose>
                <xsl:when test="$transactionName = 'Update'">
                    <xsl:apply-templates select="." mode="update"/>
                </xsl:when>
                <xsl:when test="$transactionName = 'Cancel'">
                    <xsl:apply-templates select="." mode="cancel"/>
                </xsl:when>
            </xsl:choose>
        </xsl:element>
    </xsl:template>

    <xsl:template match="api:Response" mode="update">
       <TransactionUpdate>
         <xsl:apply-templates select="api:Foo | api:Bar" mode="update"/>
         <Update/>
       <TransactionUpdate>
    </xsl:template>
    <!-- now add templates for the other elements and for other mode(s) here -->