使用xslt 2.0拆分XML并删除空属性

时间:2014-01-08 13:22:25

标签: xml xslt xsd xproc

我想从xml中删除空属性,还需要根据特定元素拆分它。我创建了两个xsl用于分割和单独删除空属性,它工作正常。但我需要结合这两个xsl,这样在删除空属性后,需要根据特定元素对xml进行拆分。

RemoveAttribute xslt:

 <xsl:template match="node()|@*">        

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

 <xsl:template match="@*[not(normalize-space(.))]">

  <xsl:if test="descendant::*/@*[not(normalize-space(.))]">
     <xsl:copy />
  </xsl:if>

 </xsl:template>
 </xsl:stylesheet>

拆分XSLT:

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

   <xsl:output omit-xml-declaration="yes" indent="yes"/>



   <xsl:template match="/*" >


   <xsl:result-document href="ure.xml">
    <xsl:element name="Employee" >
        <xsl:attribute name="xsi:schemaLocation">sample.xsd</xsl:attribute>
        <xsl:copy-of select="/Employee/*"/>          
    </xsl:element>


</xsl:result-document>

     </xsl:template>


    </xsl:stylesheet>

输入XML:

  <?xml version="1.0" encoding="UTF-8"?>
  <Enroll>
   <Department id="x1" name="">
      <members id ="" name="lio">ds</members>
   </Department>
    <Employee>
    <address id="s1" no=""></address>
    <domain id="" no="34"></domain>
    </Employee>
  </Enroll> 

output_one xml:

   <Department id="x1" name="">
      <members id ="" name="lio">ds</members>
   </Department>

输出+ _two Xml:

   <Employee>
    <address id="s1" ></address>
    <domain  no="34"></domain>
   </Employee>

输出应该是一个两个独立的xml文件,应该有分割的xml部分,需要删除空属性。

我尝试过使用Apply-templates,include和xml pipelining,但是我无法使用它。

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:2)

这应该完成你所描述的内容:

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  exclude-result-prefixes="xsi">

  <xsl:output omit-xml-declaration="yes" indent="yes"/>

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

  <xsl:template match="Employee//@*[not(normalize-space())]" />

  <xsl:template match="/*" >

    <xsl:result-document href="output_one.xml">
      <xsl:apply-templates select="Department" />
    </xsl:result-document>

    <xsl:result-document href="output_two.xml">
      <xsl:apply-templates select="Employee" />
    </xsl:result-document>

  </xsl:template>

</xsl:stylesheet>

在提供的输入上运行时,结果为:

output_one.xml:

<Department id="x1" name="">
    <members id="" name="lio">ds</members>
</Department>

output_two.xml:

<Employee>
  <address id="s1" />
  <domain no="34" />
</Employee>

答案 1 :(得分:2)

我将提供一个XProc替代方案,因为您使用XProc标记了问题。以下内容保留了Employee元素:

<p:declare-step version="1.0" xmlns:p="http://www.w3.org/ns/xproc">
    <p:input port="source"/>
    <p:output port="result"/>

    <p:delete match="@*[normalize-space(.) = '']"/>
    <p:filter select="//Employee"/>
</p:declare-step>

您可以使用命令行执行XMLCalabash,如:

calabash --input source=in.xml --output result=employee.xml test.xpl

它确实假设您的输入中只有一个Employee元素。否则,它将尝试将多个Employee元素写入单个文件。它首先会抱怨结果输出端口不接受序列。

如果你向它添加sequence="true"而没有任何进一步的更改,你将得到非格式良好的XML,就像JLRishe的XSLT方法一样。在这种情况下,您需要使用p:wrap-sequence包装Employee元素序列,或者使用p:for-each,以及p:store之类的内容将各个员工写入磁盘。

注意:如果您是XProc的新手,最后一段可能有点简洁。如果我需要详细说明,请告诉我。

<强>增加:

如果要使用XProc保存Department和Employee元素,可以使用以下命令:

<p:declare-step version="1.0" xmlns:p="http://www.w3.org/ns/xproc">

    <p:input port="source"/>
    <p:output port="employees">
        <p:pipe step="employees" port="result"/>
    </p:output>
    <p:output port="departments" primary="true"/>

    <p:delete match="@*[normalize-space(.) = '']" name="cleaned"/>

    <p:filter select="//Employee"/>
    <p:wrap-sequence wrapper="Employees" name="employees"/>

    <p:filter select="//Department">
        <p:input port="source">
            <p:pipe step="cleaned" port="result"/>
        </p:input>
    </p:filter>
    <p:wrap-sequence wrapper="Departments"/>

</p:declare-step>

您可以使用命令行执行XMLCalabash,如:

calabash --input source=in.xml --output employees=employees.xml --output departments.xml test2.xpl

代码仍然遵循相同的流程,但员工输出端口是非主要的,并且必须明确绑定到employees步骤的结果。部门过滤使用显式输入端口绑定,不将“员工”的结果作为输入,而是使用“已清理”步骤。所有其他输入和输出都根据约定自动绑定。

注意:我添加了p:wrap-sequence以使其更强大。如果您将员工的名称属性从p:wrap-sequence移至其前面的p:filter,则可以将其删除。

HTH!