从某些已定义的XPATH生成XML

时间:2015-04-24 14:06:05

标签: xml xslt xpath saxon xalan

我正在尝试根据一些已定义的XPATH从另一个XML生成XML。

XPATH:

country/name,
country/org_id,
country/lang,
country/currency,
generate_date,
schedule/category/id,
schedule/category/name,
schedule/category/classes/class/id,
schedule/category/classes/class/duration,
schedule/category/classes/class/price,
schedule/category/classes/class/instruction_language

Xpath不包括根节点的名称,它是一个列表。

XML:

<?xml version="1.0" encoding="utf-8" ?>
<ou_schedule>
  <country>
    <name>Country Name</name>
    <org_id>Org ID</org_id>
    <lang>language</lang>
    <currency>Currency</currency>
  </country>
  <generate_date>Date</generate_date>
  <schedule>
    <category>
      <id>cat id</id>
      <name>Cat name</name>
      <classes>
        <class>
          <id>class id</id>
          <duration>class duration</duration>
          <price>price</price>
          <instruction_language>Test Data</instruction_language>
        </class>
        <class>
          <id>class id</id>
          <duration>class duration</duration>
          <price>price</price>
          <instruction_language>Test Data</instruction_language>
        </class>
      </classes>
    </category>
  </schedule>
</ou_schedule>

输出:

<?xml version="1.0" encoding="utf-8"?>
<ou_schedule>
  <country.name>country name</country.name>
  <country.org_id>org id</country.org_id>
  <country.lang>language</country.lang>
  <country.currency>currency</country.currency>
  <generate_date>date</generate_date>
  <schedule.category.name>Cat Name</schedule.category.name>
  <schedule.category.id>Cat ID</schedule.category.id>
  <schedule.category.classes.class.id>class id</schedule.category.classes.class.id>
  <schedule.category.classes.class.duration>class duration</schedule.category.classes.class.duration>
  <schedule.category.classes.class.price>price</schedule.category.classes.class.price>
  <schedule.category.classes.class.instruction_language>Test Data</schedule.category.classes.class.instruction_language>

  <country.name>country name</country.name>
  <country.org_id>org id</country.org_id>
  <country.lang>language</country.lang>
  <country.currency>currency</country.currency>
  <generate_date>date</generate_date>
  <schedule.category.name>Cat Name</schedule.category.name>
  <schedule.category.id>Cat ID</schedule.category.id>
  <schedule.category.classes.class.id>class id</schedule.category.classes.class.id>
  <schedule.category.classes.class.duration>class duration</schedule.category.classes.class.duration>
  <schedule.category.classes.class.price>price</schedule.category.classes.class.price>
  <schedule.category.classes.class.instruction_language>Test Data</schedule.category.classes.class.instruction_language>
</ou_schedule>

这里,为了消除歧义,我使用除了根节点之外的祖先命名节点名称,即与XPATH相同,但用/替换.

是否可以使用通用XSLT

实现此目的

2 个答案:

答案 0 :(得分:2)

  

是否可以使用某些通用XSLT实现此目的?

如果有两个解决方案:一个用于XSLT 1.0,另一个用于XSLT 2.0,则可能(相当人为地)将它们组合成一个,使用XSLT 2.0条件编译技术,将在“预先排除”之前排除编译时“XSLT 1.0解决方案的模板和声明。另一方面,XSLT 1.0解决方案将以向前兼容模式运行,并且还将为其模板指定更高的优先级(高于XSLT 2.0解决方案模板的优先级),因此不会选择XSLT 2.0解决方案的模板执行时,使用XSLT 1.0处理器运行转换。

可以认为这是一个有趣的练习,并按照Michael Kay“XSLT 2.0和XPath 2.0”一书中的示例,第3章:“样式表结构”,“编写便携式样式表”一节,小节:“条件编译”。示例(在我的版本中)位于第128页。

这是一个简短的XSLT 2.0解决方案(如果省略参数值,则为18行),纯(无扩展函数),不使用显式XSLT条件指令或任何{{1} }。甚至不使用xsl:variable函数:

tokenize()

解决方案2

这里将资源(文件)的URI(文件路径)作为参数传递。该文件包含所有想要的XPath表达式 - 每个表达式都在一个单独的行上。

<xsl:stylesheet version="2.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 <xsl:param name="pPaths" as="xs:string+" select=
  "'country/name',
   'country/org_id',
   'country/lang',
   'country/currency',
   'generate_date',
   'schedule/category/id',
   'schedule/category/name',
   'schedule/category/classes/class/id',
   'schedule/category/classes/class/duration',
   'schedule/category/classes/class/price',
   'schedule/category/classes/class/instruction_language'"/>

  <xsl:template match="/*">
    <xsl:copy><xsl:apply-templates/></xsl:copy>
  </xsl:template>

  <xsl:template match=
   "*/*[string-join((ancestor::*[position() ne last()]| .)/name(), '/') = $pPaths]">
    <xsl:element 
       name="{string-join((ancestor::*[position() ne last()]|.)/name(), '.')}">
      <xsl:value-of select="."/>
    </xsl:element>
  </xsl:template>
  <xsl:template match="text()"/>
</xsl:stylesheet>

解决方案3

以前的解决方案都可以进一步优化和简化,如果对于输入XPath表达式,已知它们选择具有单个文本节点子元素的元素(这是原始提供的输入XPath表达式的情况,并提供源XML文档):

<xsl:stylesheet version="2.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 <xsl:param name="pFilePath" select="'file:///C:/temp/expressions.txt'"/>

 <xsl:variable name="vExprs" select="tokenize(unparsed-text($pFilePath), '\r?\n')"/>

  <xsl:template match="/*">
    <xsl:copy><xsl:apply-templates/></xsl:copy>
  </xsl:template>

  <xsl:template match=
   "*/*[string-join((ancestor::*[position() ne last()]| .)/name(), '/') = $vExprs]">
    <xsl:element name=
       "{string-join((ancestor::*[position() ne last()]|.)/name(), '.')}">
      <xsl:value-of select="."/>
    </xsl:element>
  </xsl:template>
  <xsl:template match="text()"/>
</xsl:stylesheet>

答案 1 :(得分:0)

我的第一个想法是:有趣的是,在这里我们将获得一个动态构建的XSL转换。但这似乎无法实现,因为dynamic xpath in xslt解释道。

因此,需要第二个想法:您可以将XSL转换视为XPATH表达式列表。从这个意义上讲,您只需要一个XSLT文件,如下所示

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

    <!-- the following select-attributes are the set of XPATH expressions 
         (relative to /ou_schedule/schedule/category/classes/class) -->
    <xsl:template name="XPathList">
        <category_name>
            <xsl:apply-templates select="ancestor::category/name"/>
        </category_name>

        <category_id>
            <xsl:apply-templates select="ancestor::category/id"/>
        </category_id>

        <id>
            <xsl:apply-templates select="id"/>
        </id>

        <duration>
            <xsl:apply-templates select="duration"/>
        </duration>

        <price>
            <xsl:apply-templates select="price"/>
        </price>

        <instruction_language>
            <xsl:apply-templates select="instruction_language"/>
        </instruction_language>
    </xsl:template>

    <!-- Basis -->
    <xsl:template match="/">
        <ou_schedule>
            <xsl:apply-templates select="//class"/>
        </ou_schedule>
    </xsl:template>

    <xsl:template match="class">
        <xsl:copy>
            <xsl:call-template name="XPathList"/>
        </xsl:copy>    
    </xsl:template>
</xsl:stylesheet>

嗯,人们可以用更紧凑的方式编写这种转换。但我们的目标是转换&#34;拥有一系列XPATH来转换XML&#34;代码。