-1

I am trying to read the XML content from file and convert the content in to dictionary then I need to modify the content of the Dictionary and create a new XML from the updated dictionary

<?xml version="1.0"?>
<catalog>
   <book id="bk101">
      <author>Gambardella, Matthew</author>
      <title>XML Developer's Guide</title>
      <genre>Computer</genre>
      <price>44.95</price>
      <publish_date>2000-10-01</publish_date>
      <description>An in-depth look at creating applications 
      with XML.</description>
   </book>
   <book id="bk102">
      <author>Ralls, Kim</author>
      <title>Midnight Rain</title>
      <genre>Fantasy</genre>
      <price>5.95</price>
      <publish_date>2000-12-16</publish_date>
      <description>A former architect battles corporate zombies, 
      an evil sorceress, and her own childhood to become queen 
      of the world.</description>
   </book>
  </catalog>

The above is my sample XML , I need to get the below XML after converting.

<?xml version="1.0"?>
<catalog>
   <book id="bk101">
      <author>Test User/author>
      <title>XML Developer's Guide</title>
      <genre>Computer</genre>
      <price>44.95</price>
      <publish_date>2000-10-01</publish_date>
      <description>An in-depth look at creating applications 
      with XML.</description>
   </book>
   <book id="bk102">
      <author>Ralls, Kim</author>
      <title>Midnight Rain</title>
      <genre>Fantasy</genre>
      <price>5.95</price>
      <publish_date>2000-12-16</publish_date>
      <description>A former architect battles corporate zombies, 
      an evil sorceress, and her own childhood to become queen 
      of the world.</description>
   </book>
  </catalog>

Sample code I am using

with open("books.xml") as xml_file:
    data_dict = xmltodict.parse(xml_file.read())
    xml_file.close()


data_dict['catalog']['book'][0]['author']="testuser"
xml = dicttoxml(data_dict,attr_type=False,root=False)
dom = parseString(xml)
print(dom.toprettyxml())
xmlfile = open("newbook.xml", "w")
xmlfile.write(dom.toprettyxml())
xmlfile.close()

I am expected to get back the XML is same format as the source

But with above code , the output is not coming as same

Any suggestions are welcome

Deepak
  • 11
  • 3

1 Answers1

0

Here is how to do via XSLT.

The XSLT below is pretty generic. It is following a so called Identity Transform pattern.

The XSLT is expecting two parameters:

  • id attribute of the book where you need to change its author.
  • author element new value.

How to transform an XML file using XSLT in Python

I added a Python code directly to the answer. It shows how to pass both parameters dynamically at run-time.

Input XML

<?xml version="1.0"?>
<catalog>
    <book id="bk101">
        <author>Gambardella, Matthew</author>
        <title>XML Developer's Guide</title>
        <genre>Computer</genre>
        <price>44.95</price>
        <publish_date>2000-10-01</publish_date>
        <description>An in-depth look at creating applications 
      with XML.</description>
    </book>
    <book id="bk102">
        <author>Ralls, Kim</author>
        <title>Midnight Rain</title>
        <genre>Fantasy</genre>
        <price>5.95</price>
        <publish_date>2000-12-16</publish_date>
        <description>A former architect battles corporate zombies, 
      an evil sorceress, and her own childhood to become queen 
      of the world.</description>
    </book>
</catalog>

XSLT

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

    <xsl:param name="id" select="'bk101'"/>
    <xsl:param name="author" select="'Test Author'"/>

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

    <xsl:template match="/catalog/book[@id=$id]/author">
        <xsl:copy>
            <xsl:value-of select="$author"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Output XML

<?xml version='1.0' encoding='utf-8' ?>
<catalog>
  <book id="bk101">
    <author>Test Author</author>
    <title>XML Developer's Guide</title>
    <genre>Computer</genre>
    <price>44.95</price>
    <publish_date>2000-10-01</publish_date>
    <description>An in-depth look at creating applications 
      with XML.
    </description>
  </book>
  <book id="bk102">
    <author>Ralls, Kim</author>
    <title>Midnight Rain</title>
    <genre>Fantasy</genre>
    <price>5.95</price>
    <publish_date>2000-12-16</publish_date>
    <description>A former architect battles corporate zombies, 
      an evil sorceress, and her own childhood to become queen 
      of the world.
    </description>
  </book>
</catalog>

Python

import os
import lxml.etree as ET

inputfile = "D:\\temp\\input.xml"
xsltfile = "D:\\temp\\process.xslt"
outfile = "D:\\output\\output.xml"



dom = ET.parse(inputfile)
xslt = ET.parse(xsltfile)
transform = ET.XSLT(xslt)
newdom = transform(dom,
              id=XSLT.strparam("bk101"),
              author=XSLT.strparam("New Author"))
infile = unicode((ET.tostring(newdom, pretty_print=True)))
outfile = open(outfile, 'a')
outfile.write(infile)
Yitzhak Khabinsky
  • 18,471
  • 2
  • 15
  • 21
  • One doubt. How we can convert the XML to XSLT in runtime..? Because I was thinking about keeping my solution will work for any type of XMLs – Deepak Dec 06 '21 at 06:19
  • I updated my answer. Check it out. I included a generic Python code for XSLT transformation, and showed how to pass parameters dynamically at run-time to XSLT. – Yitzhak Khabinsky Dec 06 '21 at 12:52
  • Getting {XSLTParseError}Failed to compile predicate error on the line transform = ET.XSLT(xslt) And for using unicode do we need to import any package..? @Yitzhak Khabinsky – Deepak Dec 06 '21 at 13:56
  • `import os` and `import lxml.etree as ET` – Yitzhak Khabinsky Dec 06 '21 at 14:26
  • i am using the same code which u given but seeing this error{XSLTParseError}Failed to compile predicate on the transform = ET.XSLT(xslt) line @Yitzhak Khabinsky – Deepak Dec 06 '21 at 14:32
  • And i am using the same sample xslt file u given as well – Deepak Dec 06 '21 at 14:44
  • Please try `newdom = transform(dom)` without explicit parameters. There are defaults in the XSLT. – Yitzhak Khabinsky Dec 06 '21 at 14:52
  • But I am not getting the transform itself as this code is throwing error . transform = ET.XSLT(xslt) [XSLTParseError}Failed to compile predicate ] Appreciate your responses , I am newbie to XML parsing. @Yitzhak Khabinsky – Deepak Dec 06 '21 at 14:58
  • My answer includes a link to a working sample of XSLT transformation in Python. – Yitzhak Khabinsky Dec 06 '21 at 14:59