使用XSLT将具有公共头节点和多个主体节点的XML拆分为多个文件

时间:2014-09-19 15:18:40

标签: xml file xslt split transformation

我有一个XML文档,其结构类似于以下示例:

<p:Document versione="1.0"
  xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 
  xmlns:p="http://www.fatturapa.gov.it/sdi/fatturapa/v1.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <DocumentHeader>
     global header
  </DocumentHeader>
  <DocumentBody>
     body 1
  </DocumentBody>
  <DocumentBody>
     body 2
  </DocumentBody>
</p:Document>

请注意,“全局标题”和“正文X”可能会出现嵌套的xml块,而不仅仅是纯文本。

我需要将此XML文件拆分为 - 在此示例中 - 两个XML文件,如下所示:

<p:Document versione="1.0"    
  xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 
  xmlns:p="http://www.fatturapa.gov.it/sdi/fatturapa/v1.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <DocumentHeader>
     global header
  </DocumentHeader>
  <DocumentBody>
     body 1
  </DocumentBody>
</p:Document>

<p:Document versione="1.0"
  xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 
  xmlns:p="http://www.fatturapa.gov.it/sdi/fatturapa/v1.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <DocumentHeader>
     global header
  </DocumentHeader>
  <DocumentBody>
     body 2
  </DocumentBody>
</p:Document>

通常,我必须创建几个新的XML,每个DocumentBody节点一个,在每个创建的新文件中放置相同的DocumentHeader节点。

我认为XSL转换是最好的方法,但我不知道如何。 我尝试使用报告的示例here,但这些情况不管理“标头”节点。

<xsl:template match="/root">
  <xsl:for-each select="DocumentBody">
    <xsl:result-document method="xml" href="file_{@id}-output.xml">
      <root>
        <xsl:copy-of select="/root/@*" />
        <DocumentBody>
          <xsl:copy-of select="../@* | ." />
        </DocumentBody>
      </root>
    </xsl:result-document>
  </xsl:for-each>
</xsl:template> 

你能帮助我吗?


收到第一个回答后更新。 我使用建议的代码

通过Saxon工具执行转换
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
            xmlns:p="http://example.com/p">
  <xsl:template match="/p:Document">
    <xsl:for-each select="DocumentBody">
      <xsl:result-document method="xml" href="file_{position()}-output.xml">
        <p:Document>
          <xsl:copy-of select="/*/@*, ../DocumentHeader, ." />
        </p:Document>
      </xsl:result-document>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

我注意到原始xml和其中一个新文档之间存在以下差异。 原文(部分内容):

<p:Document versione="1.0" 
xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 
xmlns:p="http://www.fatturapa.gov.it/sdi/fatturapa/v1.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <DocumentHeader> 

文件1(部分):

<p:Document
xmlns:p="http://www.fatturapa.gov.it/sdi/fatturapa/v1.0" versione="1.0">
<DocumentHeader 
    xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

也许我在这个选择中需要差异表达式?

<xsl:copy-of select="/*/@*, ../DocumentHeader, ." />

谢谢!

2 个答案:

答案 0 :(得分:0)

将您的示例XML更正为名称空间良好的形式:

<p:Document versione="1.0" xmlns:p="http://example.com/p">
  <DocumentHeader>
     global header
  </DocumentHeader>
  <DocumentBody>
     body 1
  </DocumentBody>
  <DocumentBody>
     body 2
  </DocumentBody>
</p:Document>

以下XSLT 2.0样式表应该满足您的要求:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
                xmlns:p="http://example.com/p">
  <xsl:template match="/p:Document">
    <xsl:for-each select="DocumentBody">
      <xsl:result-document method="xml" href="file_{position()}-output.xml">
        <p:Document>
          <xsl:copy-of select="/*/@*, ../DocumentHeader, ." />
        </p:Document>
      </xsl:result-document>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

copy-of将三件事复制到结果文档中 - 原始根元素的属性,DocumentHeader和当前DocumentBody。如上所述,假设DocumentHeader和DocumentBody元素不在命名空间中,如果它们是,那么您需要在样式表中使用合适的前缀声明它并相应地调整XPath表达式。

在此上下文中,position()函数为您提供了一个基本上是&#34;迭代计数器的数字&#34;对于for-each - 1表示第一个DocumentBody,2表示第二个,等等。

答案 1 :(得分:0)

以下转变 - 强烈基于@ IanRoberts的 - 应该最终解决问题:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
    xmlns:p="http://www.fatturapa.gov.it/sdi/fatturapa/v1.0">
  <xsl:template match="/p:Document">
    <xsl:for-each select="DocumentBody">
      <xsl:result-document method="xml" 
        href="{substring-before(base-uri(),'.xml')}_doc_n°_{position()}.xml">
        <p:Document versione="1.0" 
            xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 
            xmlns:p="http://www.fatturapa.gov.it/sdi/fatturapa/v1.0" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <xsl:copy-of select="/*/@*, ../DocumentHeader, ." />
        </p:Document>
      </xsl:result-document>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

假设原始xml名为example.xml,新文件的名称为“example_doc_n°1.xml”等。 注意:对于对意大利电子发票感兴趣的人,名为“Fattura Elettronica”,请将文件替换为FatturaElettronica。