XML format change using XSL file in a Python code

2019-09-19 10:56发布

Have written a Python code to transform a XML file to a particular format using XSL stylesheet. Python code below:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from lxml import etree
def transform(xmlPath, xslPath):
  # read xsl file
  xslRoot = etree.fromstring(open(xslPath).read())
  transform = etree.XSLT(xslRoot)
  # read xml
  xmlRoot = etree.fromstring(open(xmlPath).read())
  # transform xml with xslt
  transRoot = transform(xmlRoot)
  # return transformation result
  return etree.tostring(transRoot)
if __name__ == '__main__':
  print(transform('talendtest.xml', 'style2.xsl'))

style2.xsl content:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:user="http://mycompany.com/mynamespace" version="1.0">

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

  <xsl:template match="/">
    <xsl:for-each select="/rowData/rows">
      <xsl:value-of select="Plan"/>
      <xsl:text>|</xsl:text>
      <xsl:value-of select="Purpose"/>
      <xsl:text>|</xsl:text>
      <xsl:value-of select="Description"/>
      <xsl:text>|</xsl:text>
      <xsl:value-of select="Travel_Date"/>
      <xsl:text>|</xsl:text>
      <xsl:value-of select="Length_of_trip_days"/>
      <xsl:text>|</xsl:text>
      <xsl:value-of select="Air_Travel"/>
      <xsl:text>|</xsl:text>
      <xsl:value-of select="Total_Travel"/>
      <xsl:text>|</xsl:text>
      <xsl:value-of select="Total_Airfare"/>
      <xsl:text>|</xsl:text>
      <xsl:value-of select="Total_Hotel"/>
      <xsl:text>|</xsl:text>
      <xsl:value-of select="Total_Meals"/>
      <xsl:text>|</xsl:text>
      <xsl:value-of select="Total_Rental_Car"/>
      <xsl:text>|</xsl:text>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

XML file content:

<?xml version="1.0" encoding="UTF-8"?>

<call method="SOME" callerName="Travel_call">
  <credentials login="abc.com" password="XXXXX" instanceCode="YYYY"></credentials>
  <importDataOptions planOrActuals="Plan" allowParallel="false" moveBPtr="false" useMappings="true"></importDataOptions>
  <version name="API Test Version" isDefault="false"></version>
  <sheet name="Travel" isUserAssigned="false"></sheet>
  <rowData>
    <header>Plan|Purpose|Air Travel|Description|Travel Date|Length of Trip (Days)|Travel Costs &gt;|Total Travel|Total Airfare|Total Hotel|Total Meals|Total Rental Car</header>
    <rows>
      <Plan>Sales - North</Plan>
      <Purpose>Seminar/Conference</Purpose>
      <Description>Conf</Description>
      <Travel_Date>1/11/2015</Travel_Date>
      <Length_of_trip_days>3</Length_of_trip_days>
      <Air_travel>Yes - International</Air_travel>
      <Total_Travel>2455.0</Total_Travel>
      <Total_Airfare>1300.0</Total_Airfare>
      <Total_Hotel>750.0</Total_Hotel>
      <Total_Meals>255.0</Total_Meals>
      <Total_Rental_Car>150.0</Total_Rental_Car>
    </rows>
   </rowData>
</call>

Desired file content:

<call method="SOME" callerName="Travel_call">
  <credentials login="abc.com" password="XXXXX" instanceCode="YYYY"></credentials>
  <importDataOptions planOrActuals="Plan" allowParallel="false" moveBPtr="false" useMappings="true"></importDataOptions>
  <version name="API Test Version" isDefault="false"></version>
  <sheet name="Travel" isUserAssigned="false"></sheet>
  <rowData>
    <header>Plan|Purpose|Air Travel|Description|Travel Date|Length of Trip (Days)|Travel Costs|Total Travel|Total Airfare|Total Hotel|Total Meals|Total Rental Car</header>
    <rows>
        <row> Sales - North|Seminar/Conference|Conf|1/11/2015|3|Yes - International|2455.0|1300.0|750.0|255.0|150.0</row>
    </rows>
  </rowData>
</call>

However when I run the program python xslapply.py the output I receive is None. There are no other details displayed. Please can someone help with if there is an issue with the XML file or the XSL styling applied which is not returning any actual result.

标签: python xml xslt
1条回答
我想做一个坏孩纸
2楼-- · 2019-09-19 11:20

Your XSLT returns an empty string, because:

<xsl:for-each select="/rowData/rows">

does not select anything (because rowData is not the root element, and not the child of the current node). Try changing it to:

<xsl:for-each select="call/rowData/rows">

There are other problems. Why is your output method set to text, when your expected output is XML? And you're not doing anything to output the other elements besides rowData.


Added:

To add a row for every record set (i.e rows), try it this way:

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

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="rowData">
    <xsl:copy>
        <xsl:copy-of select="header"/>
        <rows>
            <xsl:apply-templates select="rows"/>
        </rows>
    </xsl:copy>
</xsl:template> 

<xsl:template match="rows">
    <row>
        <xsl:value-of select="concat(Plan, '|', Purpose, '|', Description, '|', Travel_Date, '|', Length_of_trip_days, '|', Air_Travel, '|', Total_Travel, '|', Total_Airfare, '|', Total_Hotel, '|', Total_Meals, '|', Total_Rental_Car)" />
    </row>
</xsl:template> 

</xsl:stylesheet>

Note: if every rows has exactly the elements listed in the header, and in the same order, you could make the last template simply:

<xsl:template match="rows">
    <row>
        <xsl:for-each select="*">
            <xsl:value-of select="."/>
        </xsl:for-each>
        <xsl:if test="position()!=last()">
            <xsl:text>|</xsl:text>
        </xsl:if>
    </row>
</xsl:template> 
查看更多
登录 后发表回答