首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对CSV执行XML的泛型XSLT --几乎已经实现了,但仍然停留在

对CSV执行XML的泛型XSLT --几乎已经实现了,但仍然停留在
EN

Stack Overflow用户
提问于 2016-11-12 18:54:23
回答 1查看 377关注 0票数 0

我已经从这些论坛收集了这个XSLT的零碎部分。我试图将它们放在一起来创建一个通用的XSLT,通过指定应该包含在CSV文件中的节点的路径来将XML转换为CSV。

我有三件事,经过大约10个小时的搅乱,我仍然搞不清楚。

  1. 我想对csv:columns中命名的每个列进行迭代。在每次迭代期间,我需要提取和存储列的text()。我认为这是迭代的方法,但要确保:
  2. 一旦获得了列中的text(),就需要将其放入列名变量中,使其在与getNodeValue一起使用时工作。

无法使用变量设置列名。如果我没有硬编码这个值(用撇号包围),我就无法让它工作。这就是我在代码中有以下一行的原因:

代码语言:javascript
复制
     <xsl:variable name="columnname" select="'location/city'" />
  1. 我希望将getNodeValue的结果传递给商值,以便正确引用结果。

XSLT:

代码语言:javascript
复制
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:csv="csv:csv" xpath-default-namespace="http://nowhere/" >
<xsl:output method="text" encoding="utf-8" />
<xsl:strip-space elements="*" />

<xsl:variable name="delimiter" select="','" />

<csv:columns>
    <column>title</column>
    <column>location/city</column>
</csv:columns>

<xsl:template match="job">
   <xsl:value-of select="concat(@id, ',')"/>

   <!-- #1 I WANT TO LOOP THROUGH ALL OF THE CSV COLUMNS HERE -->

   <!-- #2 How do I put the text into the variable 'columnname' variable so that it works with getNodeValue? -->
   <xsl:variable name="columnname" select="'location/city'" />
   <xsl:variable name="vXpathExpression" select="$columnname"/>

   <xsl:call-template name="getNodeValue">
       <xsl:with-param name="pExpression" select="$vXpathExpression"/>
   </xsl:call-template>

   <!-- #3 After getNodeValue gets the value, I want to send that value into 'quotevalue' -->

   <xsl:text>&#xa;</xsl:text>
</xsl:template>

<xsl:template name="getNodeValue">
  <xsl:param name="pExpression"/>
  <xsl:param name="pCurrentNode" select="."/>

  <xsl:choose>
   <xsl:when test="not(contains($pExpression, '/'))">
     <xsl:value-of select="$pCurrentNode/*[name()=$pExpression]"/>
   </xsl:when>
   <xsl:otherwise>
     <xsl:call-template name="getNodeValue">
       <xsl:with-param name="pExpression"
         select="substring-after($pExpression, '/')"/>
       <xsl:with-param name="pCurrentNode" select=
       "$pCurrentNode/*[name()=substring-before($pExpression, '/')]"/>
     </xsl:call-template>
   </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="quotevalue">
   <xsl:param name="value"/>
   <xsl:choose>
   <!-- Quote the value if required -->
       <xsl:when test="contains($value, '&quot;')">
           <xsl:variable name="x" select="replace($value, '&quot;',  '&quot;&quot;')"/>
           <xsl:value-of select="concat('&quot;', $x, '&quot;')"/>
       </xsl:when>
       <xsl:when test="contains($value, $delimiter)">
           <xsl:value-of select="concat('&quot;', $value, '&quot;')"/>
       </xsl:when>
       <xsl:otherwise>
           <xsl:value-of select="$value"/>
       </xsl:otherwise>
   </xsl:choose>
</xsl:template>

示例XML

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<positionfeed
   xmlns="http://nowhere/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    version="2006-04">
   <job id="2830302">
       <employer>Acme</employer>
       <title>Manager</title>
       <description>Full time</description>
       <postingdate>2016-09-15T23:12:13Z</postingdate>
       <location>
          <city>Los Angeles</city>
          <state>California</state>
       </location>
    </job>
    <job id="2830303">
       <employer>Acme</employer>
       <title>Clerk, evenings</title>
       <description>Part time</description>
       <postingdate>2016-09-15T23:12:13Z</postingdate>
       <location>
          <city>Albany</city>
          <state>New York</state>
       </location>
    </job>
</positionfeed>

使用我提供的XSLT提供的当前输出

代码语言:javascript
复制
2830302,Los Angeles
2830303,Albany

如果XSLT按需要工作,则输出

代码语言:javascript
复制
2830302,Manager,Los Angeles
2830303,"Clerk, evenings",Albany

解决方案(非常感谢下面的提姆帮助)

代码语言:javascript
复制
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:csv="csv:csv" xpath-default-namespace="http://www.job-search-engine.com/add-jobs/positionfeed-namespace/" >

    <xsl:output method="text" encoding="utf-8" />
    <xsl:strip-space elements="*" />

    <!-- Set the value of the delimiter character -->
    <xsl:variable name="delimiter" select="','" />

    <!-- The name of the node that contains the column values -->
    <xsl:param name="containerNodeName" select="'job'"/>

    <!-- All nodes that should be ignored during processing -->
    <xsl:template match="source|feeddate"/>

    <!-- The names of the nodes to be included in the CSV file -->
    <xsl:variable name="columns" as="element()*">
       <column header="Title">title</column>
       <column header="Category">category</column>
       <column header="Description">description</column>
       <column header="PostingDate">postingdate</column>
       <column header="URL">joburl</column>
       <column header="City">location/city</column>
       <column header="State">location/state</column>
    </xsl:variable>

    <!-- ************** DO NOT TOUCH BELOW **************** -->
    <!-- ************** DO NOT TOUCH BELOW **************** -->
    <!-- ************** DO NOT TOUCH BELOW **************** -->
    <!-- ************** DO NOT TOUCH BELOW **************** -->
    <!-- ************** DO NOT TOUCH BELOW **************** -->

    <!-- Warn about unmatched nodes -->
    <xsl:template match="*">
       <xsl:message terminate="no">
          <xsl:text>WARNING: Unmatched element: </xsl:text>
          <xsl:value-of select="name()"/>
       </xsl:message>

       <xsl:apply-templates/>
    </xsl:template>

    <!-- Generate the column headers -->
    <xsl:template match="//*[*[local-name()=$containerNodeName]]">
       <xsl:value-of select="'Id'"/>
       <xsl:value-of select="$delimiter"/>
       <xsl:for-each select="$columns/@header">
       <xsl:variable name="colname" select="." />
           <xsl:value-of select="$colname"/>
            <xsl:if test="position() != last()">
                <xsl:value-of select="$delimiter"/>
            </xsl:if>
       </xsl:for-each>
       <xsl:text>&#xa;</xsl:text>
       <xsl:apply-templates />
    </xsl:template>

    <!-- Generate the rows of column data -->
    <xsl:template match="//*[local-name()=$containerNodeName]">

       <!-- TODO: Handle attributes generically -->
       <xsl:value-of select="@id"/>

       <xsl:variable name="container" select="." />
       <xsl:for-each select="$columns">
           <xsl:value-of select="$delimiter"/>
           <xsl:variable name="vXpathExpression" select="."/>
           <xsl:call-template name="getQuotedNodeValue">
               <xsl:with-param name="pCurrentNode" select="$container"/>
               <xsl:with-param name="pExpression" select="$vXpathExpression"/>
           </xsl:call-template>
       </xsl:for-each>
       <xsl:text>&#xa;</xsl:text>
    </xsl:template>

    <xsl:template name="getQuotedNodeValue">
      <xsl:param name="pExpression"/>
      <xsl:param name="pCurrentNode" select="."/>

      <xsl:choose>
       <xsl:when test="not(contains($pExpression, '/'))">
         <xsl:variable name="result" select="$pCurrentNode/*[name()=$pExpression]"/>
         <xsl:call-template name="quotevalue">
           <xsl:with-param name="value" select="$result"/>
         </xsl:call-template>
       </xsl:when>
       <xsl:otherwise>
         <xsl:call-template name="getQuotedNodeValue">
           <xsl:with-param name="pExpression" select="substring-after($pExpression, '/')"/>
           <xsl:with-param name="pCurrentNode" select= "$pCurrentNode/*[name()=substring-before($pExpression, '/')]"/>
         </xsl:call-template>
       </xsl:otherwise>
      </xsl:choose>
    </xsl:template>

    <xsl:template name="quotevalue">
       <xsl:param name="value"/>
       <xsl:choose>
           <xsl:when test="contains($value, '&quot;')">
               <!-- Quote the value and escape the double-quotes -->
               <xsl:variable name="x" select="replace($value, '&quot;',  '&quot;&quot;')"/>
               <xsl:value-of select="concat('&quot;', $x, '&quot;')"/>
           </xsl:when>
           <xsl:otherwise>
                <!-- Quote the value -->
               <xsl:value-of select="concat('&quot;', $value, '&quot;')"/>
           </xsl:otherwise>
       </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

示例数据演示解决方案

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<positionfeed
    xmlns="http://www.job-search-engine.com/add-jobs/positionfeed-namespace/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.job-search-engine.com/add-jobs/positionfeed-namespace/ http://www.job-search-engine.com/add-jobs/positionfeed.xsd"
    version="2006-04">
<source>Casting360</source>
<feeddate>2016-11-11T21:48:34Z</feeddate><job id="1363612">
<employer>Casting360</employer>
<title>The Robert Irvine Show Is Seeking Guests</title>
<category>Reality TV</category>
<description>TV personality ROBERT IRVINE (Restaurant Impossible) is seeking guests looking for solutions to their unique problems to share their stories on his show!

Our next show is Thursday, September 22nd in LA. If you're not in LA we will provide your airfare, hotel, car service, and per diem. 

Please note: WE ARE NOT LOOKING FOR RESUMES; THIS IS NOT AN ACTING GIG. We are looking for real people to share their stories!

*appearance fee (TBD)

If you or someone you know has a conflict that they need help resolving, WE WANT TO HEAR FROM YOU. 

Please email tvgal.ri@gmail.com the following information:
Name
Phone number
Your story in 2-3 paragraphs
1-3 photos of yourself.</description>
<postingdate>2016-09-15T23:12:13Z</postingdate>
<joburl>http://casting360.com/lgj/8886644624?jobid=1363612&amp;city=Los+Angeles&amp;state=CA</joburl>
<location>
<nation>USA</nation>
<city>Los Angeles</city>
<state>California</state>
</location>
<jobsource>Casting360</jobsource>
</job><job id="1370302">
<employer>Casting360</employer>
<title>Photoshoot for Publication</title>
<category>Modeling</category>
<description>6 FEMALE Models are wanted for publication photoshoot.

If you're not in the NYC Vicinity (NY, Pa, Ct,) DO NOT REPLY because your response will be summarily ignored.

Chosen models will be given a 5 look photo shoot. The shoot will occur on location (outdoors) in highly public locations chosen both for it's convenience and scenery.

The 5 looks (outfits) will be pre-determined by our staff of items most outfits within a model's wardrobe. 

THIS IS A TF (UNPAID) SHOOT.  After the release of the magazine, the photos agreed upon from the shoot shall be given to the model (in digital format) for her to build her portfolio. 

Chosen models will receive a 5 outfit photo shoot at no cost to them by a NY Fashion Photographer.As a result, chosen  models not only receive a free photo shoot, but also become PUBLISHED MODELS featured in a magazine. 
The model (Janeykay) centered in the photo attached (Please look at the attached photo) is a Casting360 member who not only received her photo shoot, not only is being featured in a magazine, but also made the cover becoming a Cover Model from her shoot with us.</description>
<postingdate>2016-10-03T00:34:43Z</postingdate>
<joburl>http://casting360.com/lgj/8886644624?jobid=1370302&amp;city=New+York&amp;state=NY</joburl>
<location>
<nation>USA</nation>
<city>New York</city>
<state>New York</state>
</location>
<jobsource>Casting360</jobsource>
</job><job id="1370962">
<employer>Casting360</employer>
<title>Actresses Needed for &quot;Red Shore&quot;, Action Film</title>
<category>Acting</category>
<description>CASTING (non-union)
We are a New Independent company looking to shoot our first feature. We are currently looking to fill two Major roles.

Female/African American, Hispanic, Asian, Pacific Islander/ 5'5-5'10/ Age Late 30's-Early 40's.

Project description: A long standing feud between two best friends turned enemies escalates over a valuable Diamond on display in a New York City Museum. With the stakes high they each seek the help of both friends and strangers to settle their feud once and for all. 

Please note this is a non-paid project. 
Fight training will be provided for free. 

Please email including age and height in your e-mail.
Those selected will be invited to our audition.</description>
<postingdate>2016-10-03T14:18:20Z</postingdate>
<joburl>http://casting360.com/lgj/8886644624?jobid=1370962&amp;city=New+York&amp;state=NY</joburl>
<location>
<nation>USA</nation>
<city>New York</city>
<state>New York</state>
</location>
<jobsource>Casting360</jobsource>
</job>
</positionfeed>
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-11-12 23:33:47

在使用XSLT2.0时,可以使用如下所示的变量定义列:

代码语言:javascript
复制
<xsl:variable name="columns" as="element()*">
    <column>title</column>
    <column>location/city</column>
</xsl:variable>

然后,您只需使用一个简单的语句来迭代它们。

代码语言:javascript
复制
<xsl:for-each select="$columns">

但您可能遇到的问题是,在这个xsl:for-each中,您已经更改了上下文。您不再定位在job元素上,而是位于column元素上,并且不希望表达式相对于该元素。您确实需要切换回job元素,这可以简单地通过在job之前设置一个对xsl:for-each元素的变量引用,然后将其作为命名模板的参数来实现:

代码语言:javascript
复制
<xsl:template match="job">
   <xsl:value-of select="@id"/>
   <xsl:variable name="job" select="." />
   <xsl:for-each select="$columns">
       <xsl:value-of select="$delimiter"/>
       <xsl:variable name="vXpathExpression" select="."/>
       <xsl:call-template name="getNodeValue">
           <xsl:with-param name="pCurrentNode" select="$job"/>
           <xsl:with-param name="pExpression" select="$vXpathExpression"/>
       </xsl:call-template>
   </xsl:for-each>
   <xsl:text>&#xa;</xsl:text>
</xsl:template>

至于引用结果,不只是xsl:value-of,只需调用以值作为参数的quote模板即可。

代码语言:javascript
复制
 <xsl:when test="not(contains($pExpression, '/'))">
   <xsl:call-template name="quotevalue">
     <xsl:with-param name="value" select="$pCurrentNode/*[name()=$pExpression]" />
   </xsl:call-template>
 </xsl:when>

编辑:如果您想要一个列名的标题行,则必须匹配job节点的父节点,然后只输出$column变量的值。

代码语言:javascript
复制
<xsl:template match="*[job]">
    <xsl:value-of select="$columns" separator="," />
    <xsl:text>&#xa;</xsl:text>
    <xsl:apply-templates />
</xsl:template>

或者如果你不想走完这条路

代码语言:javascript
复制
<xsl:value-of select="$columns/(tokenize(., '/')[last()])" separator="," />

或者您可以扩展columns变量,使其具有标题文本。

代码语言:javascript
复制
<xsl:variable name="columns" as="element()*">
    <column header="Title">title</column>
    <column header="City">location/city</column>
</xsl:variable>

那你就这么做..。

代码语言:javascript
复制
 <xsl:value-of select="$columns/@header" separator="," />
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40566474

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档