通用XSLT转换来操作类似的XML数据

时间:2013-05-14 22:30:30

标签: xslt

我们有一个生成不需要的XML元素的工具,我们正在使用XSLT将其转换为所需的格式。

我们正在为该文件生成的每个XML编写不同的XSLT。例如一个用于Customers XML,一个用于Orders XML,依此类推。

下面是该工具生成的几个XML数据文件和预期的实际输出

客户

工具生成的XML

<Message>
  <Data>
    <CustomerArray>
      <Customer>
        <X>
          <Name>John</Name>
          <Id>100</Id>
          <Roles>
            <Role>
              <X>Manager</X>
              <X>Architect</X>
            </Role>
          </Roles>
        </X>
        <X>
          <Name>Doe</Name>
          <Id>102</Id>
          <Roles>
            <Role>
              <X>Supervisor</X>
              <X>Admin</X>
            </Role>
          </Roles>
        </X>
      </Customer>
    </CustomerArray>
  </Data>
</Message>

必需的XML数据

<Message>
  <Data>
    <CustomerArray>
      <Customer>
        <Name>John</Name>
        <Id>100</Id>
        <Roles>
          <Role>Manager</Role>
          <Role>Architect</Role>
        </Roles>
      </Customer>
      <Customer>
        <Name>Doe</Name>
        <Id>102</Id>
        <Roles>
          <Role>Supervisor</Role>
          <Role>Admin</Role>
        </Roles>
      </Customer>
    </CustomerArray>
  </Data>
</Message>

订单

工具生成的XML

<Message>
  <Orders>
    <Order>
      <X>
        <OrderNumber>O123</OrderNumber>
        <CustomerID>C100</CustomerID>
        <Quantity>100</Quantity>
        <UnitPrice>10.0</UnitPrice>
      </X>
      <X>
        <OrderNumber>O456</OrderNumber>
        <CustomerID>C107</CustomerID>
        <Quantity>100</Quantity>
        <UnitPrice>5.0</UnitPrice>
      </X>
    </Order>
  </Orders>
</Message>

必需的XML数据

<Message>
  <Orders>
    <Order>
      <OrderNumber>O123</OrderNumber>
      <CustomerID>C100</CustomerID>
      <Quantity>100</Quantity>
      <UnitPrice>10.0</UnitPrice>
    </Order>
    <Order>
      <OrderNumber>O456</OrderNumber>
      <CustomerID>C107</CustomerID>
      <Quantity>100</Quantity>
      <UnitPrice>5.0</UnitPrice>
    </Order>
  </Orders>
</Message>

不需要的元素X可以来自任何级别。

是否可以编写通用XSLT转换以在所有XML输入中实现此结果?例如,在找到X的地方,将其替换为父标记,然后删除父标记。

2 个答案:

答案 0 :(得分:2)

您需要为具有任何X子节点的所有节点编写带有显式模板的标识转换,以便可以复制它们。

这个变换符合你的要求。它使用变量name来保存X父元素的名称,以避免在生成每个输出元素时编写更加模糊的name(current())

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:strip-space elements="*"/>
  <xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="yes"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="*[X]">
    <xsl:variable name="name" select="name()"/>
    <xsl:for-each select="X">
      <xsl:element name="{$name}">
        <xsl:apply-templates/>
      </xsl:element>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

<强>输出

客户

<Message>
   <Data>
      <CustomerArray>
         <Customer>
            <Name>John</Name>
            <Id>100</Id>
            <Roles>
               <Role>Manager</Role>
               <Role>Architect</Role>
            </Roles>
         </Customer>
         <Customer>
            <Name>Doe</Name>
            <Id>102</Id>
            <Roles>
               <Role>Supervisor</Role>
               <Role>Admin</Role>
            </Roles>
         </Customer>
      </CustomerArray>
   </Data>
</Message>

订单

<Message>
   <Orders>
      <Order>
         <OrderNumber>O123</OrderNumber>
         <CustomerID>C100</CustomerID>
         <Quantity>100</Quantity>
         <UnitPrice>10.0</UnitPrice>
      </Order>
      <Order>
         <OrderNumber>O456</OrderNumber>
         <CustomerID>C107</CustomerID>
         <Quantity>100</Quantity>
         <UnitPrice>5.0</UnitPrice>
      </Order>
   </Orders>
</Message>

答案 1 :(得分:1)

这是一个稍微简单/更短的解决方案,当X可以包含非X兄弟元素时,它也能正确处理:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="*[X]"><xsl:apply-templates/></xsl:template>

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

当此转换应用于以下XML文档时(第一个提供的文档,其中一个非X兄弟添加到X元素

<Message>
  <Data>
    <CustomerArray>
      <Customer>
        <X>
          <Name>John</Name>
          <Id>100</Id>
          <Roles>
            <Role>
              <X>Manager</X>
              <X>Architect</X>
            </Role>
          </Roles>
        </X>
        <somethingElse/>
        <X>
          <Name>Doe</Name>
          <Id>102</Id>
          <Roles>
            <Role>
              <X>Supervisor</X>
              <X>Admin</X>
            </Role>
          </Roles>
        </X>
      </Customer>
    </CustomerArray>
  </Data>
</Message>

产生了想要的正确结果

<Message>
   <Data>
      <CustomerArray>
         <Customer>
            <Name>John</Name>
            <Id>100</Id>
            <Roles>
               <Role>Manager</Role>
               <Role>Architect</Role>
            </Roles>
         </Customer>
         <somethingElse/>
         <Customer>
            <Name>Doe</Name>
            <Id>102</Id>
            <Roles>
               <Role>Supervisor</Role>
               <Role>Admin</Role>
            </Roles>
         </Customer>
      </CustomerArray>
   </Data>
</Message>

请注意,Borodin的解决方案会丢失somethingElse元素。