XML format change using XSL file in a Python code

不问归期 提交于 2020-01-17 05:08:14

问题


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.


回答1:


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> 


来源:https://stackoverflow.com/questions/33998774/xml-format-change-using-xsl-file-in-a-python-code

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!