1

我从 Cognos 获得了一个 XML 文档输出,我想将其用作 Crystal 报表的输入。但是,Crystal Report 所需的 XML 格式与 Cognos 输出的 XML 格式不同。

我正在尝试使用 XSLT 转换输入 XML 文档 (Cognos) 以获得 Crystal 所需的 XML。

设置上下文后,下面是来自 Cognos 的输入 XML:

<?xml version="1.0"?>
<dataset>
<metadata>
    <item Name="EmpId" />
    <item Name="EmpName" />
    <item Name="DeptName" />
</metadata>
<data>
    <rows>
        <row>
            <value>1</value>
            <value>John</value>
            <value>Finance</value>
        </row>
        <row>
            <value>2</value>
            <value>Peter</value>
            <value>Admin</value>
        </row>
    </rows>
</data>

Crystal Report 所需的所需 XML 格式:

<?xml version="1.0"?>
<dataset>
<row>
    <EmpId>1</EmpId>
    <EmpName>John</EmpName>
    <DeptName>Finance</DeptName>
</row>
<row>
    <EmpId>2</EmpId>
    <EmpName>Peter</EmpName>
    <DeptName>Admin</DeptName>
</row>
</dataset>

我在 XSLT 下面写了所需的转换:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<dataset>
<xsl:for-each select="./dataset/data/rows/row">
    <row>
        <xsl:for-each select="/dataset/metadata/item">
            <xsl:element name="{@Name}">
                <xsl:for-each select="/dataset/data/rows/row/value">
                    <xsl:value-of select="."/>
                </xsl:for-each>                 
            </xsl:element>
        </xsl:for-each>             
    </row>
</xsl:for-each>
</dataset>
</xsl:template>
</xsl:stylesheet>

我得到低于输出:

<?xml version="1.0" encoding="UTF-16"?>
<dataset>
<row>
    <EmpId>1JohnFinance2PeterAdmin</EmpId>
    <EmpName>1JohnFinance2PeterAdmin</EmpName>
    <DeptName>1JohnFinance2PeterAdmin</DeptName>
</row>
<row>
    <EmpId>1JohnFinance2PeterAdmin</EmpId>
    <EmpName>1JohnFinance2PeterAdmin</EmpName>
    <DeptName>1JohnFinance2PeterAdmin</DeptName>
</row>

我哪里错了?

4

3 回答 3

5

这个简短而简单的转换

<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:variable name="vNames" select="/*/metadata/*/@Name"/> 

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

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

 <xsl:template match="row/*">
  <xsl:variable name="vPos" select="position()"/>
  <xsl:element name="{$vNames[$vPos]}"><xsl:apply-templates/></xsl:element>
 </xsl:template>
</xsl:stylesheet>

当应用于提供的 XML 文档时(添加了一个缺少的结束标记以使其格式正确):

<dataset>
    <metadata>
        <item Name="EmpId" />
        <item Name="EmpName" />
        <item Name="DeptName" />
    </metadata>
    <data>
        <rows>
            <row>
                <value>1</value>
                <value>John</value>
                <value>Finance</value>
            </row>
            <row>
                <value>2</value>
                <value>Peter</value>
                <value>Admin</value>
            </row>
        </rows>
    </data>
</dataset>

产生想要的正确结果:

<dataset>
   <row>
      <EmpId>1</EmpId>
      <EmpName>John</EmpName>
      <DeptName>Finance</DeptName>
   </row>
   <row>
      <EmpId>2</EmpId>
      <EmpName>Peter</EmpName>
      <DeptName>Admin</DeptName>
   </row>
</dataset>

说明

  1. 使用模板和 XSLT 模板选择机制来完成这项工作。作为 XSLT 中的一项规则,我们更喜欢xsl:apply-templates——xsl:for-each从而获得更简单、更可扩展、更易理解和可维护的代码。这是一个几乎 100% 的“推送式”解决方案的示例。

  2. xsl:variable用于获取(一次且永远)我们将不断使用的节点。

  3. 保存position()在变量中以供以后在其他上下文中使用 -position()取决于上下文。

于 2012-09-13T12:10:11.477 回答
1

我会使用<template>and<apply-templates>机制来解决这个问题。For-each我认为这不是去这里的正确方式。

XML 输入:

<?xml version="1.0"?>
<dataset>
<metadata>
    <item Name="EmpId" />
    <item Name="EmpName" />
    <item Name="DeptName" />
</metadata>
<data>
    <rows>
        <row>
            <value>1</value>
            <value>John</value>
            <value>Finance</value>
        </row>
        <row>
            <value>2</value>
            <value>Peter</value>
            <value>Admin</value>
        </row>
    </rows>
</data>
</dataset>  

将此样式表应用于它:

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

<xsl:template match="/">
    <dataset>
        <xsl:apply-templates select="//row"/>
    </dataset>
</xsl:template>

<xsl:template match="row">
    <row>
        <xsl:apply-templates select="value"/>
    </row>
</xsl:template>

<xsl:template match="value">
    <xsl:variable name="index">
        <xsl:number/>
    </xsl:variable>
    <xsl:element name="{../../../../metadata/item[position() = $index]/@Name}">
        <xsl:apply-templates select="@* | node()"/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

你得到这个输出:

<?xml version="1.0" encoding="utf-8"?>
<dataset>
<row>
    <EmpId>1</EmpId>
    <EmpName>John</EmpName>
    <DeptName>Finance</DeptName>
</row>
<row>
    <EmpId>2</EmpId>
    <EmpName>Peter</EmpName>
    <DeptName>Admin</DeptName>
</row>
</dataset>

我通过匹配和应用某些模板创建的dataset和元素。row

于 2012-09-13T11:49:52.370 回答
1

在自定义@DimitreNovatchev提供的解决方案后,我能够获得所需的 XML 转换。

输入 XML

<?xml version="1.0"?>
<dataset  xmlns="http://developer.cognos.com/schemas/xmldata/1/"  xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
<metadata>
    <item name="Employee Id" />
    <item name="Employee Name" />
    <item name="Department Name" />
</metadata>
<data>      
    <row>
        <value>1</value>
        <value Salutation="Dr." >John</value>
        <value>Finance</value>
    </row>
    <row>
        <value>2</value>
        <value Salutation="Mr." >Peter</value>
        <value>Admin</value>
    </row>      
</data>

XSLT 转换

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:c="http://developer.cognos.com/schemas/xmldata/1/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vNames" select="/*/c:metadata/*/@name" />
<xsl:template match="/*/c:data">
<dataset>
    <xsl:apply-templates/>
</dataset>
</xsl:template>
<xsl:template match="c:row">
<row>
    <xsl:apply-templates/>
</row>
</xsl:template>
<xsl:template match="c:row/*">
<xsl:variable name="vPos" select="position()"/>
<xsl:element name="{translate($vNames[$vPos], ' ', '_')}">
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="@*">
<xsl:attribute name="{name()}">
    <xsl:value-of select="." />
</xsl:attribute>
</xsl:template>

</xsl:样式表>

输出 XML

<?xml version="1.0" encoding="UTF-16"?>
<dataset xmlns:c="http://developer.cognos.com/schemas/xmldata/1/">
<row>
    <Employee_Id>1</Employee_Id>
    <Employee_Name Salutation="Dr.">John</Employee_Name>
    <Department_Name>Finance</Department_Name>
</row>
<row>
    <Employee_Id>2</Employee_Id>
    <Employee_Name Salutation="Mr.">Peter</Employee_Name>
    <Department_Name>Admin</Department_Name>
</row>
于 2012-09-18T05:49:57.337 回答