需要编写一个XSLT来将XML转换为具有特定数据的CSV。

huangapple go评论69阅读模式
英文:

need to write an xslt to transform xml to csv with specific data

问题

以下是修改后的XSLT代码,它只生成与标题字段映射中的字段匹配的数据:

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

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

  <xsl:template match="/">
    <!-- Header row -->
    <xsl:for-each select="root/headerFieldsMap/*">
      <xsl:value-of select="."/>
      <xsl:if test="position() != last()">
        <xsl:value-of select="$delimiter"/>
      </xsl:if>
    </xsl:for-each>
    <xsl:text>&#10;</xsl:text>

    <!-- Data rows -->
    <xsl:for-each select="root/psPositions/position">
      <xsl:for-each select="root/headerFieldsMap/*">
        <xsl:variable name="fieldName" select="."/>
        <xsl:variable name="fieldValue" select="ancestor::position/*[local-name() = $fieldName]"/>

        <!-- If the field value is StdHoursFreq and it's equal to 'W', replace it with 'Weekly' -->
        <xsl:choose>
          <xsl:when test="$fieldName = 'StdHoursFreq' and $fieldValue = 'W'">
            <xsl:text>Weekly</xsl:text>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="$fieldValue"/>
          </xsl:otherwise>
        </xsl:choose>

        <xsl:if test="position() != last()">
          <xsl:value-of select="$delimiter"/>
        </xsl:if>
      </xsl:for-each>
      <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

这个修改后的XSLT代码会根据标题字段映射仅生成相应的数据,就像您所期望的那样。

英文:

I'm trying to write an xslt which creates a csv from an xml. I need to create a header field mapping such that header matches the data.
Here is an example xml

  &lt;headerFieldsMap&gt;
    &lt;BusinessUnit&gt;Business Unit Code&lt;/BusinessUnit&gt;
    &lt;CompanyName&gt;Business Unit Desct&lt;/CompanyName&gt;
    &lt;DeptDescr&gt;Department Description&lt;/DeptDescr&gt;
    &lt;DeptId&gt;Department&lt;/DeptId&gt;
    &lt;DescrShort&gt;Posting Job Title&lt;/DescrShort&gt;
    &lt;Description&gt;Working Position Description&lt;/Description&gt;
    &lt;JobFamily&gt;Category&lt;/JobFamily&gt;
    &lt;PositionNbr&gt;Position-Nbr&lt;/PositionNbr&gt;
    &lt;StdHoursFreq&gt;Standard hours Frequency&lt;/StdHoursFreq&gt;
    &lt;TotalWorkingHours&gt;Standard Hours&lt;/TotalWorkingHours&gt;
  &lt;/headerFieldsMap&gt;
  &lt;psPositions&gt;
    &lt;position&gt;
      &lt;LocationPostal&gt;06511&lt;/LocationPostal&gt;
      &lt;ConfidentialFlag&gt;N&lt;/ConfidentialFlag&gt;
      &lt;DeptDescr&gt;New Haven EMU Shop&lt;/DeptDescr&gt;
      &lt;Description&gt;Carman E Rate&lt;/Description&gt;
      &lt;RegTemp&gt;R&lt;/RegTemp&gt;
      &lt;SalAdminPlan&gt;TWUR&lt;/SalAdminPlan&gt;
      &lt;JobCodeSetid&gt;SHARE&lt;/JobCodeSetid&gt;
      &lt;LocationCountry&gt;US&lt;/LocationCountry&gt;
      &lt;Remote&gt;N&lt;/Remote&gt;
      &lt;Step&gt;0&lt;/Step&gt;
      &lt;SalaryRangeTo&gt;0&lt;/SalaryRangeTo&gt;
      &lt;PositionNbr&gt;01000333&lt;/PositionNbr&gt;
      &lt;SalaryRangeFrom&gt;0&lt;/SalaryRangeFrom&gt;
      &lt;EffStatus&gt;A&lt;/EffStatus&gt;
      &lt;FullPartTime&gt;F&lt;/FullPartTime&gt;
      &lt;PositionStatus&gt;Approved&lt;/PositionStatus&gt;
      &lt;LocationSetId&gt;SHARE&lt;/LocationSetId&gt;
      &lt;CompanyName&gt;Metro-North Railroad&lt;/CompanyName&gt;
      &lt;RemainingHeadCount&gt;0&lt;/RemainingHeadCount&gt;
      &lt;PayFrequency&gt;H&lt;/PayFrequency&gt;
      &lt;JobCode&gt;26448E&lt;/JobCode&gt;
      &lt;RegRegion&gt;USA&lt;/RegRegion&gt;
      &lt;Shift&gt;2&lt;/Shift&gt;
      &lt;ReportsToPos&gt;01000474&lt;/ReportsToPos&gt;
      &lt;DeptIdSetId&gt;MNCRR&lt;/DeptIdSetId&gt;
      &lt;TotalWorkingHours&gt;40&lt;/TotalWorkingHours&gt;
      &lt;DescrShort&gt;Carman E R&lt;/DescrShort&gt;
      &lt;StdHoursFreq&gt;W&lt;/StdHoursFreq&gt;
      &lt;LocationCode&gt;NEW HAVEN&lt;/LocationCode&gt;
      &lt;Effdt&gt;2023-02-21&lt;/Effdt&gt;
      &lt;LocationDescr&gt;98 Union Street&lt;/LocationDescr&gt;
      &lt;LocationCity&gt;New Haven&lt;/LocationCity&gt;
      &lt;Grade&gt;004&lt;/Grade&gt;
      &lt;JobFamily&gt;Transportation Operations&lt;/JobFamily&gt;
      &lt;DeptId&gt;44302&lt;/DeptId&gt;
      &lt;LastUpdDtTm&gt;2023-02-21T19:12:05Z&lt;/LastUpdDtTm&gt;
      &lt;LocationRegion&gt;CT&lt;/LocationRegion&gt;
      &lt;ReportsToEmail&gt;Burns@mnr.org&lt;/ReportsToEmail&gt;
      &lt;PayCurrency&gt;USD&lt;/PayCurrency&gt;
      &lt;MaxHeadCount&gt;1&lt;/MaxHeadCount&gt;
      &lt;BusinessUnit&gt;MNCRR&lt;/BusinessUnit&gt;
      &lt;JobFamilyCode&gt;TRNOPS&lt;/JobFamilyCode&gt;
      &lt;CompanyCode&gt;MNR&lt;/CompanyCode&gt;
      &lt;CurrHeadCount&gt;1&lt;/CurrHeadCount&gt;
    &lt;/position&gt;
 &lt;/psPositions&gt;
&lt;/root&gt;

The csv or the output should look like this

Business Unit Code,Business Unit Desct,Department Description,Department,Posting Job Title,Working Position Description,Category,Position-Nbr,Standard hours Frequency,Standard Hours
MNCRR,Metro-North Railroad,New Haven EMU Shop,44302,Carman E R,Carman E Rate,Transportation Operations,01000333,W,40
MTAHQ,MTA Headquarters,Division of Management /Budget,400300,SR FINANCI,Sr Financial Analyst - Budgets,Finance/Accounting,01055644,W,37.5

Note that the data only has the values for the fields which are mentioned in the <headerFieldsMap> field. The fields in the <headerFieldsMap> may vary, there could only be 2 fields that come in, the data should show up accordingly.

Current xslt i have is this

&lt;xsl:stylesheet version=&quot;1.0&quot; xmlns:xsl=&quot;http://www.w3.org/1999/XSL/Transform&quot;&gt;
  &lt;xsl:output method=&quot;text&quot; encoding=&quot;UTF-8&quot;/&gt;

  &lt;xsl:variable name=&quot;delimiter&quot; select=&quot;&#39;,&#39;&quot;/&gt;

  &lt;xsl:template match=&quot;/&quot;&gt;
    &lt;!-- Header row --&gt;
    &lt;xsl:for-each select=&quot;root/headerFieldsMap/*&quot;&gt;
      &lt;xsl:value-of select=&quot;.&quot;/&gt;
      &lt;xsl:if test=&quot;position() != last()&quot;&gt;
        &lt;xsl:value-of select=&quot;$delimiter&quot;/&gt;
      &lt;/xsl:if&gt;
    &lt;/xsl:for-each&gt;
    &lt;xsl:text&gt;&amp;#10;&lt;/xsl:text&gt;

    &lt;!-- Data rows --&gt;
    &lt;xsl:for-each select=&quot;root/psPositions/position&quot;&gt;
      &lt;xsl:for-each select=&quot;*&quot;&gt;
        &lt;xsl:variable name=&quot;fieldName&quot; select=&quot;name()&quot;/&gt;
        &lt;xsl:variable name=&quot;fieldValue&quot; select=&quot;.&quot;/&gt;

        &lt;!-- If the field value is StdHoursFreq and it&#39;s equal to &quot;W&quot;, replace it with &quot;Weekly&quot; --&gt;
        &lt;xsl:choose&gt;
          &lt;xsl:when test=&quot;$fieldName = &#39;StdHoursFreq&#39; and $fieldValue = &#39;W&#39;&quot;&gt;
            &lt;xsl:text&gt;Weekly&lt;/xsl:text&gt;
          &lt;/xsl:when&gt;
          &lt;xsl:otherwise&gt;
            &lt;xsl:value-of select=&quot;$fieldValue&quot;/&gt;
          &lt;/xsl:otherwise&gt;
        &lt;/xsl:choose&gt;

        &lt;xsl:if test=&quot;position() != last()&quot;&gt;
          &lt;xsl:value-of select=&quot;$delimiter&quot;/&gt;
        &lt;/xsl:if&gt;
      &lt;/xsl:for-each&gt;
      &lt;xsl:text&gt;&amp;#10;&lt;/xsl:text&gt;
    &lt;/xsl:for-each&gt;
  &lt;/xsl:template&gt;
&lt;/xsl:stylesheet&gt;

But this one prints out all the data from the xml. I only need the data that is mentioned in the header.
XSLT standards: 1.0

The above xslt generates the output that looks like the one below. It shouldn't have data for the fields that are not in the header, like "Burns@mnr.org" shouldn't be there because "ReportsToEmail" isn't there in the header.

Business Unit Code,Business Unit Desct,Department Description,Department,Posting Job Title,Working Position Description,Category,Position-Nbr,Standard hours Frequency,Standard Hours
06511,N,New Haven EMU Shop,Carman E Rate,R,TWUR,SHARE,US,N,0,0,01000333,0,A,F,Approved,SHARE,Metro-North Railroad,0,H,26448E,USA,2,01000474,MNCRR,40,Carman E R,Weekly,NEW HAVEN,2023-02-21,98 Union Street,New Haven,004,Transportation Operations,44302,2023-02-21T19:12:05Z,CT,Burns@mnr.org,USD,1,MNCRR,TRNOPS,MNR,1
10004,Y,Division of Management /Budget,Sr Financial Analyst - Budgets,R,HAY0,SHARE,US,N,0,0,01055644,0,A,F,Approved,SHARE,MTA Headquarters,1,B,1287,USA,1,01005823,MTAHQ,37.5,SR FINANCI,Weekly,2 BROADWAY,2023-02-21,2 Broadway,New York,438,Finance/Accounting,400300,2023-02-21T12:57:45Z,NY,USD,1,MTAHQ,FINANC,MTA,0

答案1

得分: 1

我只需要标题中提到的数据

尝试以下方式:

XSLT 1.0

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

<xsl:key name="row-data" match="position/*" use="concat(name(), generate-id(..))" />

<xsl:template match="/root">
    <xsl:variable name="cols" select="headerFieldsMap/*" />
    <!-- 标题行 -->
    <xsl:for-each select="$cols">
        <xsl:value-of select="." />
        <xsl:if test="position() != last()">,</xsl:if>
    </xsl:for-each>
    <xsl:text>&#10;</xsl:text>
    <!-- 数据行 -->
    <xsl:for-each select="psPositions/position">
        <xsl:variable name="row-id" select="generate-id()" />
        <xsl:for-each select="$cols">
            <xsl:value-of select="key('row-data', concat(name(), $row-id))" />
            <xsl:if test="position() != last()">,</xsl:if>
        </xsl:for-each>
        <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

请注意,从您的输入XML中获得的结果与您显示的结果非常不同。然而,样式表符合您问题中提出的逻辑(至少是我理解的方式)。


要将W更改为Weekly,您可以执行以下操作:

<xsl:for-each select="$cols">
    <xsl:variable name="value" select="key('row-data', concat(name(), $row-id))" />
    <xsl:choose>
        <xsl:when test="name() = 'StdHoursFreq' and $value = 'W'">Weekly</xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$value" />
        </xsl:otherwise>
    </xsl:choose>
    <xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
英文:

> I only need the data that is mentioned in the header

Try it along the lines of:

XSLT 1.0

&lt;xsl:stylesheet version=&quot;1.0&quot; 
xmlns:xsl=&quot;http://www.w3.org/1999/XSL/Transform&quot;&gt;
&lt;xsl:output method=&quot;text&quot; encoding=&quot;UTF-8&quot;/&gt;

&lt;xsl:key name=&quot;row-data&quot; match=&quot;position/*&quot; use=&quot;concat(name(), generate-id(..))&quot; /&gt;

&lt;xsl:template match=&quot;/root&quot;&gt;
	&lt;xsl:variable name=&quot;cols&quot; select=&quot;headerFieldsMap/*&quot;/&gt;
	&lt;!-- header row--&gt;
    &lt;xsl:for-each select=&quot;$cols&quot;&gt;
		&lt;xsl:value-of select=&quot;.&quot;/&gt;
		&lt;xsl:if test=&quot;position()!=last()&quot;&gt;,&lt;/xsl:if&gt;
    &lt;/xsl:for-each&gt;
	&lt;xsl:text&gt;&amp;#10;&lt;/xsl:text&gt;
	&lt;!-- data rows--&gt;
	&lt;xsl:for-each select=&quot;psPositions/position&quot;&gt;
		&lt;xsl:variable name=&quot;row-id&quot; select=&quot;generate-id()&quot;/&gt;
		&lt;xsl:for-each select=&quot;$cols&quot;&gt;
			&lt;xsl:value-of select=&quot;key(&#39;row-data&#39;, concat(name(), $row-id))&quot;/&gt;
			&lt;xsl:if test=&quot;position()!=last()&quot;&gt;,&lt;/xsl:if&gt;
		&lt;/xsl:for-each&gt;
		&lt;xsl:text&gt;&amp;#10;&lt;/xsl:text&gt;
	&lt;/xsl:for-each&gt;
&lt;/xsl:template&gt;

&lt;/xsl:stylesheet&gt;

Note that the result obtained from your input XML is very different from what you show. Nevertheless, the stylesheet conforms to the logic presented in your question (at least the way I understood it).


To change W to Weekly, you could do:

		&lt;xsl:for-each select=&quot;$cols&quot;&gt;
			&lt;xsl:variable name=&quot;value&quot; select=&quot;key(&#39;row-data&#39;, concat(name(), $row-id))&quot; /&gt;
			&lt;xsl:choose&gt;
				&lt;xsl:when test=&quot;name() = &#39;StdHoursFreq&#39; and $value = &#39;W&#39;&quot;&gt;Weekly&lt;/xsl:when&gt;
				&lt;xsl:otherwise&gt;
					&lt;xsl:value-of select=&quot;$value&quot;/&gt;
				&lt;/xsl:otherwise&gt;
			&lt;/xsl:choose&gt;
			&lt;xsl:if test=&quot;position()!=last()&quot;&gt;,&lt;/xsl:if&gt;
		&lt;/xsl:for-each&gt;

答案2

得分: 1

请尝试以下的XSLT。

XSLT

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" encoding="UTF-8"/>
    
    <xsl:variable name="delimiter" select="','"/>
    
    <xsl:template match="/root">
        <!-- Header row -->
        <xsl:for-each select="headerFieldsMap/*">
            <xsl:value-of select="."/>
            <xsl:if test="position() != last()">
                <xsl:value-of select="$delimiter"/>
            </xsl:if>
        </xsl:for-each>
        <xsl:text>&#xA;</xsl:text>
    
        <!-- Data rows -->
        <xsl:for-each select="psPositions/position">
            <xsl:variable name="row" select="."/>
            <xsl:for-each select="/root/headerFieldsMap/*">
                <xsl:variable name="fieldName" select="local-name()"/>
                <xsl:variable name="fieldValue" select="."/>
    
                <!-- If the field value is StdHoursFreq and it's equal to 'W', replace it with 'Weekly' -->
                <xsl:choose>
                    <xsl:when test="$row/*[local-name()=$fieldName] = 'W' and $row/*[local-name()='StdHoursFreq']">
                        <xsl:text>Weekly</xsl:text>
                    </xsl:when>
                    <xsl:when test="$fieldName = 'DescrShort'">
                        <xsl:value-of select="$row/*[local-name()='LocationPostal']"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:valueof select="$row/*[local-name()=$fieldName]"/>
                    </xsl:otherwise>
                </xsl:choose>
    
                <xsl:if test="position() != last()">
                    <xsl:value-of select="$delimiter"/>
                </xsl:if>
            </xsl:for-each>
            <xsl:text>&#xA;</xsl:text>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

如果你需要任何进一步的帮助,请随时告诉我。

英文:

Please try the following XSLT.

XSLT

&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;xsl:stylesheet version=&quot;1.0&quot; xmlns:xsl=&quot;http://www.w3.org/1999/XSL/Transform&quot;&gt;
&lt;xsl:output method=&quot;text&quot; encoding=&quot;UTF-8&quot;/&gt;
&lt;xsl:variable name=&quot;delimiter&quot; select=&quot;&#39;,&#39;&quot;/&gt;
&lt;xsl:template match=&quot;/root&quot;&gt;
&lt;!-- Header row --&gt;
&lt;xsl:for-each select=&quot;headerFieldsMap/*&quot;&gt;
&lt;xsl:value-of select=&quot;.&quot;/&gt;
&lt;xsl:if test=&quot;position() != last()&quot;&gt;
&lt;xsl:value-of select=&quot;$delimiter&quot;/&gt;
&lt;/xsl:if&gt;
&lt;/xsl:for-each&gt;
&lt;xsl:text&gt;&amp;#xA;&lt;/xsl:text&gt;
&lt;!-- Data rows --&gt;
&lt;xsl:for-each select=&quot;psPositions/position&quot;&gt;
&lt;xsl:variable name=&quot;row&quot; select=&quot;.&quot;/&gt;
&lt;xsl:for-each select=&quot;/root/headerFieldsMap/*&quot;&gt;
&lt;xsl:variable name=&quot;fieldName&quot; select=&quot;local-name()&quot;/&gt;
&lt;xsl:variable name=&quot;fieldValue&quot; select=&quot;.&quot;/&gt;
&lt;!-- If the field value is StdHoursFreq and it&#39;s equal to &quot;W&quot;, replace it with &quot;Weekly&quot; --&gt;
&lt;xsl:choose&gt;
&lt;xsl:when test=&quot;$row/*[local-name()=$fieldName] = &#39;W&#39; and $row/*[local-name()=&#39;StdHoursFreq&#39;]&quot;&gt;
&lt;xsl:text&gt;Weekly&lt;/xsl:text&gt;
&lt;/xsl:when&gt;
&lt;xsl:when test=&quot;$fieldName = &#39;DescrShort&#39;&quot;&gt;
&lt;xsl:value-of select=&quot;$row/*[local-name()=&#39;LocationPostal&#39;]&quot;/&gt;
&lt;/xsl:when&gt;
&lt;xsl:otherwise&gt;
&lt;xsl:value-of select=&quot;$row/*[local-name()=$fieldName]&quot;/&gt;
&lt;/xsl:otherwise&gt;
&lt;/xsl:choose&gt;
&lt;xsl:if test=&quot;position() != last()&quot;&gt;
&lt;xsl:value-of select=&quot;$delimiter&quot;/&gt;
&lt;/xsl:if&gt;
&lt;/xsl:for-each&gt;
&lt;xsl:text&gt;&amp;#xA;&lt;/xsl:text&gt;
&lt;/xsl:for-each&gt;
&lt;/xsl:template&gt;
&lt;/xsl:stylesheet&gt;

huangapple
  • 本文由 发表于 2023年3月1日 08:21:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75598546.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定