如何将XML属性更改为元素然后将它们移动到不同的节点?

时间:2017-05-01 16:58:13

标签: xml xslt

我想将<Card>元素(idstatustype)的属性转换为关联的<Information>元素的子元素。我已经实现了属性 - &gt;元素更改,但如何为结果元素提供正确的父元素?

以下是输入文件示例:

<?xml version="1.0" encoding="UTF-8"?>
<Customers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" hasMore="false"
    recordCount="1" responseTime="2017-04-27T19:35:02" returnCode="SUCCESS">
<Customer>
<Information>
<FirstName>Brandon</FirstName>
<MiddleName/>
<LastName>Wrong</LastName>
<CustomerStatus>ACTIVE</CustomerStatus>
<Household>false</Household>
<EnrollmentDate xsi:nil="true"/>
<Address>100 Broadway Ave</Address>
<City>San Gabriel</City>
<State>CA</State>
<ZIP>91776-2348</ZIP>
<Country/>
<Phone>6268881212</Phone>
<MobilePhone/>
<Email>BWrong1@gmail.com</Email>
<DateOfBirth xsi:nil="true"/>
</Information>
<Cards>
<Card id="40012345601" status="ACTIVE" type="Customer card"/>
</Cards>
</Customer>
</Customers>

这是我目前的样式表:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                >

  <xsl:output indent="yes" method="xml" encoding="utf-8" omit-xml-declaration="yes"/>

  <xsl:template match="*">
    <xsl:element name="{local-name()}">
      <xsl:apply-templates select="@* | node()"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="Customers/@hasMore|Customers/@recordCount|Customers/@returnCode|@xsi:nil|@responseTime" />

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


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

  <xsl:template match="comment() | text() | processing-instruction()">
    <xsl:copy/>
  </xsl:template>

</xsl:stylesheet>

1 个答案:

答案 0 :(得分:1)

当您按照移动节点的方式完成工作时,您正在以无益的方式思考。 XSL不会更改(或移动等)任何内容。相反,它根据输入文档的特征定义结果文档。

由于定义输出文档的元素的一部分是定义其内容,因此必须在该输出节点的模板的上下文中定义输出元素的子节点。对于您的特定情况,您需要一个比您现在使用的身份变换更专门针对<Information>元素量身定制的模板。

然而,在我们继续详细说明之前,我观察一下,你定义的身份变换,在三个独立的模板上传播,并且只显示元素和属性本地名称,比规范身份更冗长改造没有明显的理由或优势。这是通常的身份变换,全部在一个模板中:

<xsl:template match="node()|@*">
  <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
</xsl:template>
但是,您似乎要抑制除名称空间声明以外的所有属性以及要转换为元素的属性。在这种情况下,不是提供明确地抑制属性的模板,而是通过在身份转换上使用(仅)这种变化而不是首先避免转换它们:

<xsl:template match="node()">
  <xsl:copy>
    <!-- transform child nodes, except attributes -->
    <xsl:apply-templates select="node()"/>
  </xsl:copy>
</xsl:template>

您不需要使用此方法保留名称空间声明的特殊规定,因为xsl:copy会自动复制复制节点的名称空间节点,从而导致需要提供名称空间声明。

在此之后,<Information>元素所需的模板可以写为身份模板的变体:

<xsl:template match="Information">
  <xsl:copy>
    <!-- transform all the element's child nodes -->
    <xsl:apply-templates select="node()"/>
    <!-- include also transformations of the associated <Card> attributes -->
    <xsl:apply-templates select="../Cards/Card/@*" />
  </xsl:copy>
</xsl:template>

<Card>属性的模板可能会有,但不清楚为什么使用local-name()代替name()。但是,您似乎确实希望避免将看似无信息的<Cards><Card>元素放入输出文档中。您可以通过为<Cards>提供将其转换为空的模板来实现:

<xsl:template match="Cards"/>

把这些全部放在一起,然后,你可能会得到这样的东西:

<xsl:stylesheet version="1.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes" method="xml" encoding="utf-8"
              omit-xml-declaration="yes"/>

  <!-- attributes are not matched or selected for transformation by this
       template: -->
  <xsl:template match="node()">
    <xsl:copy>
      <xsl:apply-templates select="node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- Information elements require a special template: -->
  <xsl:template match="Information">
    <xsl:copy>
      <!-- transform all the element's child nodes -->
      <xsl:apply-templates select="node()|@*"/>
      <!-- include also transformations of the associated <Card> attributes -->
      <xsl:apply-templates select="../Cards/Card/@*" />
    </xsl:copy>
  </xsl:template>

  <!-- transform <Cards> elements to nothing -->
  <xsl:template match="Cards"/>

  <!-- applied only where matching attribute nodes are selected for
       transformation: -->
  <xsl:template match="Card/@*">
    <xsl:element name="{name()}">
      <xsl:value-of select="."/> 
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>